home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / cocktail / doc.lha / doc.doc / lalr.doc < prev    next >
Text File  |  1992-09-25  |  76KB  |  4,173 lines

  1.  
  2.  
  3.  
  4.  
  5.  
  6.  
  7.  
  8.  
  9.  
  10.  
  11. ___________________________________________________________________
  12.  
  13.  
  14.                                    Lalr - A Generator for
  15.                                    Efficient Parsers
  16.  
  17.                                    J. Grosch
  18.  
  19.  
  20. ___________________________________________________________________
  21.  
  22.  
  23.  
  24.  
  25.  
  26.  
  27.  
  28.  
  29.  
  30.  
  31.  
  32.  
  33.  
  34.  
  35.  
  36.  
  37.  
  38.  
  39.  
  40.  
  41.  
  42.  
  43.  
  44.  
  45.  
  46.  
  47.  
  48.  
  49.  
  50. ___________________________________________________________________
  51.                                    GESELLSCHAFT FUeR MATHEMATIK
  52.                                    UND DATENVERARBEITUNG MBH
  53.  
  54.                                    FORSCHUNGSSTELLE FUeR
  55.                                    PROGRAMMSTRUKTUREN
  56.                                    AN DER UNIVERSITAeT KARLSRUHE
  57. ___________________________________________________________________
  58.  
  59.  
  60.  
  61.  
  62.  
  63.  
  64.  
  65.  
  66.  
  67.  
  68.  
  69.  
  70.  
  71.  
  72.  
  73.  
  74.  
  75.                                    Project
  76.  
  77.                              Compiler Generation
  78.  
  79.          ___________________________________________________________
  80.  
  81.                    Lalr - A Generator for Efficient Parsers
  82.  
  83.  
  84.                                  Josef Grosch
  85.  
  86.  
  87.                                  Oct. 7, 1988
  88.  
  89.          ___________________________________________________________
  90.  
  91.  
  92.                                 Report No. 10
  93.  
  94.  
  95.                              Copyright c 1988 GMD
  96.  
  97.  
  98.             Gesellschaft fuer Mathematik und Datenverarbeitung mbH
  99.                 Forschungsstelle an der Universitaet Karlsruhe
  100.                           Vincenz-Priesznitz-Str. 1
  101.                                D-7500 Karlsruhe
  102.  
  103.  
  104.  
  105.  
  106.  
  107.  
  108.  
  109.  
  110.  
  111.  
  112.  
  113.  
  114.  
  115.  
  116.  
  117.  
  118.  
  119.  
  120.  
  121.  
  122.  
  123.  
  124.  
  125.  
  126.  
  127.  
  128.  
  129.  
  130.  
  131.  
  132.  
  133.  
  134.                                   LR-Parser                                  3
  135.  
  136.  
  137.  
  138.  
  139.                    Lalr - A Generator for Efficient Parsers
  140.  
  141.  
  142.  
  143.  
  144.  
  145.                                   J. Grosch
  146.  
  147.  
  148.               GMD Forschungsstelle an der Universitaet Karlsruhe
  149.  
  150.  
  151.              Vincenz-Priesznitz-Str. 1, D-7500 Karlsruhe, Germany
  152.  
  153.  
  154.                            grosch@karlsruhe.gmd.de
  155.  
  156.  
  157.  
  158.  
  159.  
  160.  
  161.  
  162.  
  163.  
  164.  
  165.  
  166.  
  167.  
  168.  
  169.  
  170.  
  171.  
  172.  
  173.  
  174.  
  175.  
  176.  
  177.  
  178.  
  179.  
  180.  
  181.  
  182.  
  183.  
  184.  
  185.  
  186.  
  187.  
  188.  
  189.  
  190.  
  191.  
  192.  
  193.  
  194.  
  195.  
  196.  
  197.  
  198.  
  199.  
  200.  
  201.                                                                              1
  202.  
  203.  
  204.                                    SUMMARY
  205.  
  206.  
  207.  
  208. Lalr is a parser generator that generates very fast and powerful parsers.  The
  209.  
  210.  
  211. design  goals have been to generate portable, table-driven parsers that are as
  212.  
  213.  
  214. efficient as possible and which include all the features needed for  practical
  215.  
  216.  
  217. applications. Like Yacc it accepts LALR(1) grammars, resolves ambiguities with
  218.  
  219.  
  220. precedence and associativity of operators, generates table-driven parsers, and
  221.  
  222.  
  223. has a mechanism for S-attribution.  Unlike Yacc it allows grammars to be writ-
  224.  
  225.  
  226. ten in extended BNF, includes automatic error reporting, recovery, and repair,
  227.  
  228.  
  229. and generates parsers in C or Modula-2.  In case of LR-conflicts, a derivation
  230.  
  231.  
  232. tree is printed instead of the involved states and items in order to  aid  the
  233.  
  234.  
  235. location  of  the problem.  The parsers are two to three times faster as those
  236.  
  237.  
  238. of Yacc.  Using a MC 68020 processor, 35,000  tokens  per  second  or  580,000
  239.  
  240.  
  241. lines  per minute can be parsed. The sources of Lalr exist in C and in Modula-
  242.  
  243.  
  244. 2.  We describe in detail the development steps of the generated parsers.   We
  245.  
  246.  
  247. show how software engineering methods like pseudo code and stepwise refinement
  248.  
  249.  
  250. can turn a parsing algorithm from a textbook into  a  complete  and  efficient
  251.  
  252.  
  253. implementation.  We  present the details of the generated parsers and show how
  254.  
  255.  
  256.  
  257.  
  258.  
  259.  
  260.  
  261.  
  262.  
  263.  
  264.  
  265.  
  266.  
  267.                                                                              2
  268.  
  269.  
  270. the performance is achieved with a relatively simple  and  short  program.  We
  271.  
  272.  
  273. discuss  important  features of the generator and finally present a comparison
  274.  
  275.  
  276. of some parser generators.
  277.  
  278.  
  279.  
  280.  
  281.  
  282.  
  283. KEY WORDS  syntactic analysis  parser generator  LALR(1) grammar
  284.  
  285.  
  286.  
  287.  
  288.  
  289.  
  290.                                  INTRODUCTION
  291.  
  292.  
  293.  
  294.      The parser generator Lalr has been developed with the aim of combining  a
  295.  
  296.  
  297. powerful  specification  technique for context-free languages with the genera-
  298.  
  299.  
  300. tion of highly efficient parsers. As the parser generator processes the  class
  301.  
  302.  
  303. of  LALR(1)  grammars,  we  chose  the  name  Lalr to express the power of the
  304.  
  305.  
  306. specification technique.
  307.  
  308.  
  309.  
  310.      The grammars may be written using extended BNF constructs.  Each  grammar
  311.  
  312.  
  313. rule  may  be associated with a semantic action consisting of arbitrary state-
  314.  
  315.  
  316. ments written in the target language. Whenever a grammar rule is recognized by
  317.  
  318.  
  319. the  generated parser, the associated semantic action is executed. A mechanism
  320.  
  321.  
  322.  
  323.  
  324.  
  325.  
  326.  
  327.  
  328.  
  329.  
  330.  
  331.  
  332.                                                                              3
  333.  
  334.  
  335. for S-attribution (only synthesized attributes) is provided to allow  communi-
  336.  
  337.  
  338. cation between the semantic actions.
  339.  
  340.  
  341.  
  342.      In case of LR-conflicts a derivation tree is printed to aid the  location
  343.  
  344.  
  345. of  the  problem.  The  conflict  can be resolved by specifying precedence and
  346.  
  347.  
  348. associativity for terminals and rules.  Syntactic  errors  are  handled  fully
  349.  
  350.  
  351. automatically  by  the  generated parsers including error reporting, recovery,
  352.  
  353.  
  354. and repair. The mentioned features are discussed in more detail in one of  the
  355.  
  356.  
  357. following sections.
  358.  
  359.  
  360.  
  361.      The generated parsers are table-driven. The comb-vector technique [ASU86]
  362.  
  363.  
  364. is  used to compress the parse tables. The sources of the generator Lalr exist
  365.  
  366.  
  367. in the languages C and Modula-2. Parsers can be generated in the  languages  C
  368.  
  369.  
  370. and  Modula-2,  too. The generator uses the algorithm described by DeRemer and
  371.  
  372.  
  373. Pennello [DeP82] to compute the  look-ahead  sets.   Currently  Lalr  runs  on
  374.  
  375.  
  376. several workstations under UNIX.
  377.  
  378.  
  379.  
  380.      Parsers generated by Lalr are two to three times as fast as Yacc  [Joh75]
  381.  
  382.  
  383. generated  ones.  They  reach  a  speed of 35,000 tokens per second or 580,000
  384.  
  385.  
  386.  
  387.  
  388.  
  389.  
  390.  
  391.  
  392.  
  393.  
  394.  
  395.  
  396.  
  397.  
  398.                                                                              4
  399.  
  400.  
  401. lines per minute on a MC 68020 processor, excluding  the  time  for  scanning.
  402.  
  403.  
  404. The  sizes  of  the  parsers  are 25 to 40% larger than those produced by Yacc
  405.  
  406.  
  407. (resulting in e. g. 37 KB for Ada).  The reason is mainly  due  to  additional
  408.  
  409.  
  410. code  for error recovery, as well as a small space penalty for the increase of
  411.  
  412.  
  413. speed.
  414.  
  415.  
  416.  
  417.      Recently some researchers  report  that  very  fast  LR  parsers  can  be
  418.  
  419.  
  420. achieved  by  generating  direct code, in which the parse tables are converted
  421.  
  422.  
  423. into executable statements.  Pennello  [Pen86]  generates  assembly  code  and
  424.  
  425.  
  426. reports  that  the  resulting  parsers run six to ten times faster than table-
  427.  
  428.  
  429. driven parsers generated by an unspecified generator.  He measured a speed  of
  430.  
  431.  
  432. 500,000  lines  per  minute  on a computer similar to a VAX 11/780 and 240,000
  433.  
  434.  
  435. lines per minute on an Intel 80286.  A disadvantage of this  solution  is  the
  436.  
  437.  
  438. increase  of  the  parser  size  by  a factor of two to four, mainly because a
  439.  
  440.  
  441. second parser is needed for error recovery.
  442.  
  443.  
  444.  
  445.      Whitney and Horspool [HoW89,WhH88] generate C code and report the genera-
  446.  
  447.  
  448. tion  of  parsers  between  five and seven times faster than those produced by
  449.  
  450.  
  451. Yacc, resulting in a speed of 95,500 to 142,000 tokens per second  or  700,000
  452.  
  453.  
  454.  
  455.  
  456.  
  457.  
  458.  
  459.  
  460.  
  461.  
  462.  
  463.  
  464.                                                                              5
  465.  
  466.  
  467. to  2  million  lines per minute for parsers for C and Pascal. The parser size
  468.  
  469.  
  470. lies in the same range of Yacc. Currently, error recovery, S-attribution,  and
  471.  
  472.  
  473. semantic  actions are not provided.  In the last section, we discuss the prob-
  474.  
  475.  
  476. lems with this kind of measurement and conclude that the results are not  com-
  477.  
  478.  
  479. parable.
  480.  
  481.  
  482.  
  483.      Currently, table-driven parsers and direct code schemes are hard to  com-
  484.  
  485.  
  486. pare.   First,  direct code schemes do not as yet implement error recovery, S-
  487.  
  488.  
  489. attribution, or semantic actions.  However, for realistic  applications  these
  490.  
  491.  
  492. features  are necessary and of course cost some time (see below).  Second, the
  493.  
  494.  
  495. speed of the direct code schemes decreases with the grammar size. We also made
  496.  
  497.  
  498. experiments with direct code parsers [KlM89] and found for example in the case
  499.  
  500.  
  501. of Ada that an efficient table-driven parser was superior both  in  speed  and
  502.  
  503.  
  504. space.   A further problem is that the code directly generated for large gram-
  505.  
  506.  
  507. mars may be too big to be translated under  the  restrictions  of  usual  com-
  508.  
  509.  
  510. pilers.
  511.  
  512.  
  513.  
  514.      According to Aszmann [Ass88] a typical compiler spends 25%  of  the  time
  515.  
  516.  
  517. for  lexical  analysis,  15%  for  syntactic  analysis,  35%  for symbol table
  518.  
  519.  
  520.  
  521.  
  522.  
  523.  
  524.  
  525.  
  526.  
  527.  
  528.  
  529.  
  530.                                                                              6
  531.  
  532.  
  533. handling and semantic analysis, and 25%  for  code  generation.   In  our  own
  534.  
  535.  
  536. experiments,  syntactic  analysis  took  5 to 10% when all compiler parts were
  537.  
  538.  
  539. constructed as efficiently as possible.  Therefore, improving the speed of the
  540.  
  541.  
  542. syntactic  analysis  phase can reduce the total compilation time by only a few
  543.  
  544.  
  545. percent. However, an engineer wants all the compiler parts to be  as  good  as
  546.  
  547.  
  548. possible.  An  engineer also wants a parser generator to have all the features
  549.  
  550.  
  551. needed for practical applications, such as error recovery, S-attribution,  and
  552.  
  553.  
  554. semantic actions.
  555.  
  556.  
  557.  
  558.      From this engineering point of view we decided to follow the table-driven
  559.  
  560.  
  561. approach. This avoids the disadvantages of the direct code parsers like assem-
  562.  
  563.  
  564. bly code, large parsers, and trouble with compiler restrictions for  big  pro-
  565.  
  566.  
  567. grams.  On the other hand, fast table-driven parsers can be generated, and can
  568.  
  569.  
  570. include error recovery, S-attribution, and semantic actions with as few  space
  571.  
  572.  
  573. and time expenses as possible.
  574.  
  575.  
  576.  
  577.      This paper shows that a speed-up between two and three times faster  than
  578.  
  579.  
  580. Yacc  is  possible  using a table-driven implementation programmed in C.  With
  581.  
  582.  
  583. this approach, the code size increases only by 25 to 40%,  mainly  because  of
  584.  
  585.  
  586.  
  587.  
  588.  
  589.  
  590.  
  591.  
  592.  
  593.  
  594.  
  595.  
  596.                                                                              7
  597.  
  598.  
  599. added  features like error recovery.  The improvement is possible by carefully
  600.  
  601.  
  602. designing the encoding of the parser actions, the details of the parsing algo-
  603.  
  604.  
  605. rithm,  and  the structure of the parse tables.  In the following we will dis-
  606.  
  607.  
  608. cuss the implementation of the generated parsers as  well  as  some  important
  609.  
  610.  
  611. features  of  the generator Lalr and present a comparison to other parser gen-
  612.  
  613.  
  614. erators.
  615.  
  616.  
  617.  
  618.  
  619.  
  620.  
  621.                              THE GENERATED PARSER
  622.  
  623.  
  624.  
  625.      This section describes the parsers generated by  Lalr.   We  develop  the
  626.  
  627.  
  628. parsing algorithm step by step as given below. We will use a pseudo code nota-
  629.  
  630.  
  631. tion except in the last step where we introduce C code.
  632.  
  633.     - basic LR-parsing algorithm
  634.     - LR(0) reductions
  635.     - encoding of the table entries
  636.     - semantic actions and S-attribution
  637.     - table representation and access
  638.     - error recovery
  639.     - mapping pseudo code to C
  640.  
  641.  
  642.  
  643.  
  644.      To be able to formally handle scanners, stacks, and grammars,  we  assume
  645.  
  646.  
  647. three modules. We only give the headings of the exported procedures consisting
  648.  
  649.  
  650.  
  651.  
  652.  
  653.  
  654.  
  655.  
  656.  
  657.  
  658.  
  659.  
  660.  
  661.                                                                              8
  662.  
  663.  
  664. out of the procedure name and the types of the parameters and results.
  665.  
  666.     MODULE scanner
  667.        PROCEDURE GetToken     (): Vocabulary
  668.        PROCEDURE GetAttribute (): <any type>
  669.  
  670.  
  671.  
  672.  
  673. Each call of the function GetToken yields the next token of the input. If  the
  674.  
  675.  
  676. input  is  read  completely,  GetToken yields the special value EndOfInput.  A
  677.  
  678.  
  679. call of the function GetAttribute returns the attributes of the current token,
  680.  
  681.  
  682. such as symbol tables indices for identifiers or values of numbers.
  683.  
  684.     MODULE stack
  685.        PROCEDURE Push         (Stack, Element)
  686.        PROCEDURE Pop          (Stack): Element
  687.        PROCEDURE Top          (Stack): Element
  688.  
  689.  
  690.  
  691.  
  692. A stack is defined as usual.
  693.  
  694.     MODULE grammar
  695.        PROCEDURE Length       (Productions): Integer
  696.        PROCEDURE LeftHandSide (Productions): Nonterminals
  697.        PROCEDURE Semantics    (Productions): <action statements>
  698.  
  699.  
  700.  
  701.  
  702. The function Length returns the length of the right-hand side of a production.
  703.  
  704.  
  705. The  function  LeftHandSide returns the nonterminal on the left-hand side of a
  706.  
  707.  
  708. production. The function Semantics maps every production to some action state-
  709.  
  710.  
  711. ments.  These statements should be executed whenever the associated production
  712.  
  713.  
  714. is recognized or reduced.
  715.  
  716.  
  717.  
  718.  
  719.  
  720.  
  721.  
  722.  
  723.  
  724.  
  725.  
  726.  
  727.                                                                              9
  728.  
  729.  
  730. Basic LR-Parsing Algorithm
  731.  
  732.  
  733.  
  734.      We start by looking in a textbook on compiler construction [ASU86,WaG84].
  735.  
  736.  
  737. (Following  [DeP82]  we  use the notion read action for what is usually called
  738.  
  739.  
  740. shift action.) An LR-Parser is controlled by a parse  table  implementing  the
  741.  
  742.  
  743. following function
  744.  
  745.  
  746.  
  747.     Table : States x Vocabulary -> Actions
  748.  
  749.  
  750.  
  751. where
  752.  
  753.  
  754.  
  755.     Actions = ( read x States ) U ( reduce x Productions ) U { halt, error }
  756.  
  757.  
  758.  
  759.  
  760.      The table controls a general LR parsing algorithm (Algorithm 1).  This is
  761.  
  762.  
  763. a  pushdown  automaton  which  remembers  the parsing of the left context in a
  764.  
  765.  
  766. stack of states. Depending on the state on top of the stack and on the  actual
  767.  
  768.  
  769. look-ahead  symbol,  it  accesses  the  parse  table and executes the obtained
  770.  
  771.  
  772. action.
  773.  
  774.  
  775.  
  776.      There are two places where the table is accessed. Depending on the second
  777.  
  778.  
  779. argument  of  the  function  Table,  we  will  call  these places terminal and
  780.  
  781.  
  782.  
  783.  
  784.  
  785.  
  786.  
  787.  
  788.  
  789.  
  790.  
  791.  
  792.                                                                             10
  793.  
  794. Algorithm 1: Basic LR parser
  795.  
  796. BEGIN
  797.    Push (StateStack, StartState)
  798.    Terminal := GetToken ()
  799.    LOOP
  800.       CASE Table (Top (StateStack), Terminal) OF
  801.       read t:     Push (StateStack, t)
  802.                   Terminal := GetToken ()
  803.       reduce p:   FOR i := 1 TO Length (p) DO
  804.                      State := Pop (StateStack)
  805.                   END
  806.                   Nonterminal := LeftHandSide (p)
  807.                   CASE Table (Top (StateStack), Nonterminal) OF
  808.                      read t: Push (StateStack, t)
  809.                   END
  810.       error:      ErrorRecovery ()
  811.       halt:       EXIT
  812.       END
  813.    END
  814. END
  815.  
  816.  
  817.  
  818. nonterminal accesses. At a terminal access, all  four  actions  are  possible.
  819.  
  820.  
  821. Nonterminals appear after reductions, only a read action is possible at a non-
  822.  
  823.  
  824. terminal access: no error action can occur. Nevertheless we use a CASE  state-
  825.  
  826.  
  827. ment  at a nonterminal access to decode the action, because there will be more
  828.  
  829.  
  830. cases in the next step.
  831.  
  832.  
  833.  
  834.  
  835.  
  836.  
  837. LR(0) Reductions
  838.  
  839.  
  840.  
  841.  
  842.  
  843.  
  844.  
  845.  
  846.  
  847.  
  848.  
  849.  
  850.  
  851.  
  852.  
  853.                                                                             11
  854.  
  855.  
  856.      The textbooks also tell us about LR(0) reductions or read-reduce  actions
  857.  
  858.  
  859. [WaG84].   For  most  languages  50% of the states are LR(0) reduce states, in
  860.  
  861.  
  862. which a reduce action is determined without examining  the  look-ahead  token.
  863.  
  864.  
  865. The introduction of a read-reduce action is probably one of the best available
  866.  
  867.  
  868. optimizations.  This saves many table accesses and considerable table space.
  869.  
  870.  
  871.  
  872.     Actions = ( read x States ) U ( reduce x Productions ) U ( read-reduce x Productions ) U { halt, error }
  873.  
  874.  
  875.  
  876.  
  877.      As we did not find an LR parsing algorithm that uses read-reduce  actions
  878.  
  879.  
  880. in  the  literature  we present our version in Algorithm 2.  The character '_'
  881.  
  882.  
  883. stands for a value that doesn't matter. A read-reduce action can occur at both
  884.  
  885.  
  886. places  of  table access. In the terminal case, we combine a read and a reduce
  887.  
  888.  
  889. action. In the nonterminal case we have to "virtually"  read  the  nonterminal
  890.  
  891.  
  892. and then to execute a reduction. This is accomplished by the inner LOOP state-
  893.  
  894.  
  895. ment. A reduce action can be followed by a series of read-reduce actions.  The
  896.  
  897.  
  898. inner LOOP statement is terminated on reaching a read action.
  899.  
  900.  
  901.  
  902.      This solution with an inner LOOP statement has two advantages: First,  as
  903.  
  904.  
  905. there  are  only  two cases within the second CASE statement, it can be turned
  906.  
  907.  
  908.  
  909.  
  910.  
  911.  
  912.  
  913.  
  914.  
  915.  
  916.  
  917.  
  918.  
  919.                                                                             12
  920.  
  921. Algorithm 2: LR parser with LR(0) reductions
  922.  
  923. BEGIN
  924.    Push (StateStack, StartState)
  925.    Terminal := GetToken ()
  926.    LOOP
  927.       CASE Table (Top (StateStack), Terminal) OF
  928.       read t:        Push (StateStack, t)
  929.                      Terminal := GetToken ()
  930.       read-reduce p: Push (StateStack, _)
  931.                      Terminal := GetToken ()
  932.                      GOTO L
  933.       reduce p:  L:  LOOP
  934.                         FOR i := 1 TO Length (p) DO
  935.                            State := Pop (StateStack)
  936.                         END
  937.                         Nonterminal := LeftHandSide (p)
  938.                         CASE Table (Top (StateStack), Nonterminal) OF
  939.                            read t:        Push (StateStack, t)
  940.                                           EXIT
  941.                            read-reduce p: Push (StateStack, _)
  942.                         END
  943.                      END
  944.       error:         ErrorRecovery ()
  945.       halt:          EXIT
  946.       END
  947.    END
  948. END
  949.  
  950.  
  951.  
  952. into an IF statement. Second, there is no need to differentiate  between  read
  953.  
  954.  
  955. and read-reduce with respect to terminal or nonterminal table access, as these
  956.  
  957.  
  958. different kinds of access occur at two different places.
  959.  
  960.  
  961.  
  962.  
  963.  
  964.  
  965.  
  966.  
  967.  
  968.  
  969.  
  970.  
  971.  
  972.  
  973.  
  974.  
  975.  
  976.  
  977.  
  978.                                                                             13
  979.  
  980.  
  981. Encoding of the Table Entries
  982.  
  983.  
  984.  
  985.      The next problem is how to efficiently represent the table entries.  Con-
  986.  
  987.  
  988. ceptually,  these  entries  are  pairs consisting of an action indicator and a
  989.  
  990.  
  991. number denoting a state or  a  production.  A  straightforward  representation
  992.  
  993.  
  994. using a record wastes too much space and is too hard to decode for interpreta-
  995.  
  996.  
  997. tion. It is advantageous to represent the table entries by simple integers  in
  998.  
  999.  
  1000. the following way:
  1001.  
  1002.  
  1003.  
  1004.     Table : States x Vocabulary -> INTEGER
  1005.  
  1006.  
  1007.  
  1008. where
  1009.  
  1010.  
  1011.     action          integer value   constant name
  1012.     _______________________________________________
  1013.     error           0               NoState
  1014.     read 1          1
  1015.     ...
  1016.     read n          n
  1017.     read-reduce 1   n + 1           FirstReadReduce
  1018.     ...
  1019.     read-reduce m   n + m
  1020.     reduce 1        n + m + 1       FirstReduce
  1021.     ...
  1022.     reduce m        n + m + m
  1023.     halt            n + m + m + 1   Stop
  1024.  
  1025.  
  1026.  
  1027.  
  1028.  
  1029.  
  1030.  
  1031.  
  1032.  
  1033.  
  1034.  
  1035.  
  1036.  
  1037.  
  1038.  
  1039.  
  1040.  
  1041.  
  1042.  
  1043.                                                                             14
  1044.  
  1045.  
  1046.      The constant n stands for the number of states and the constant m for the
  1047.  
  1048.  
  1049. number  of  productions.  As it is not possible to "read-reduce" every produc-
  1050.  
  1051.  
  1052. tion, not all numbers between n+1 and n+m are used.   The  advantage  of  this
  1053.  
  1054.  
  1055. solution  is that the table entries do not take much space, and that to decode
  1056.  
  1057.  
  1058. them the CASE statements can be turned into three simple comparisons as  shown
  1059.  
  1060.  
  1061. in  Algorithm  3.   We  neglect the actions error and halt for the moment, and
  1062.  
  1063.  
  1064. reintroduce them in later sections.
  1065.  
  1066.  
  1067.  
  1068.  
  1069.  
  1070.  
  1071. Semantic Actions and S-Attribution
  1072.  
  1073.  
  1074.  
  1075.      For the implementation of an S-attribution which is evaluated  during  LR
  1076.  
  1077.  
  1078. parsing,  the  parser has to maintain a second stack for the attribute values.
  1079.  
  1080.  
  1081. This stack grows and shrinks in parallel  with  the  existing  stack  for  the
  1082.  
  1083.  
  1084. states.  Algorithm 4 shows how these features have to be added.
  1085.  
  1086.  
  1087.  
  1088.      In order to be able to access the attributes of all right-hand side  sym-
  1089.  
  1090.  
  1091. bols,  we  need a stack with direct access, because the attribute for the i-th
  1092.  
  1093.  
  1094. symbol (counting from 1) has to be accessed by
  1095.  
  1096.  
  1097.  
  1098.  
  1099.  
  1100.  
  1101.  
  1102.  
  1103.  
  1104.  
  1105.  
  1106.  
  1107.  
  1108.                                                                             15
  1109.  
  1110. Algorithm 3: LR parser with actions encoded by integers
  1111.  
  1112. BEGIN
  1113.    Push (StateStack, StartState)
  1114.    Terminal := GetToken ()
  1115.    LOOP
  1116.       State := Table (Top (StateStack), Terminal)
  1117.       IF State >= FirstReadReduce THEN          /* reduce or read-reduce? */
  1118.          IF State < FirstReduce THEN            /* read-reduce */
  1119.             Push (StateStack, _)
  1120.             Terminal := GetToken ()
  1121.             Production := State - FirstReadReduce + 1
  1122.          ELSE                                   /* reduce */
  1123.             Production := State - FirstReduce + 1
  1124.          END
  1125.          LOOP                                   /* reduce */
  1126.             FOR i := 1 TO Length (Production) DO
  1127.                State := Pop (StateStack)
  1128.             END
  1129.             Nonterminal := LeftHandSide (Production)
  1130.             State := Table (Top (StateStack), Nonterminal)
  1131.             IF State < FirstReadReduce THEN     /* read */
  1132.                Push (StateStack, State)
  1133.                EXIT
  1134.             ELSE                                /* read-reduce */
  1135.                Push (StateStack, _)
  1136.             END
  1137.          END
  1138.       ELSE                                      /* read */
  1139.          Push (StateStack, State)
  1140.          Terminal := GetToken ()
  1141.       END
  1142.    END
  1143. END
  1144.  
  1145.  
  1146.  
  1147.  
  1148.  
  1149.  
  1150.  
  1151.  
  1152.  
  1153.  
  1154.  
  1155.  
  1156.  
  1157.  
  1158.  
  1159.  
  1160.  
  1161.  
  1162.  
  1163.  
  1164.  
  1165.  
  1166.  
  1167.  
  1168.  
  1169.  
  1170.                                                                             16
  1171. Algorithm 4: LR parser with action statements and S-attribution
  1172.  
  1173. BEGIN
  1174.    Push (AttributeStack, _)
  1175.    Push (StateStack, StartState)
  1176.    Terminal := GetToken ()
  1177.    LOOP
  1178.       State := Table (Top (StateStack), Terminal)
  1179.       IF State >= FirstReadReduce THEN          /* reduce or read-reduce? */
  1180.          IF State < FirstReduce THEN            /* read-reduce */
  1181.             Push (AttributeStack, GetAttribute ())
  1182.             Push (StateStack, _)
  1183.             Terminal := GetToken ()
  1184.             Production := State - FirstReadReduce + 1
  1185.          ELSE                                   /* reduce */
  1186.             Production := State - FirstReduce + 1
  1187.          END
  1188.          LOOP                                   /* reduce */
  1189.             FOR i := 1 TO Length (Production) DO
  1190.                Dummy := Pop (AttributeStack)
  1191.                State := Pop (StateStack)
  1192.             END
  1193.             Nonterminal := LeftHandSide (Production)
  1194.             Semantics (Production) ()
  1195.             State := Table (Top (StateStack), Nonterminal)
  1196.             IF State < FirstReadReduce THEN     /* read */
  1197.                Push (AttributeStack, SynAttribute)
  1198.                Push (StateStack, State)
  1199.                EXIT
  1200.             ELSE                                /* read-reduce */
  1201.                Push (AttributeStack, SynAttribute)
  1202.                Push (StateStack, _)
  1203.             END
  1204.          END
  1205.       ELSE                                      /* read */
  1206.          Push (AttributeStack, GetAttribute ())
  1207.          Push (StateStack, State)
  1208.          Terminal := GetToken ()
  1209.       END
  1210.    END
  1211. END
  1212.  
  1213.     AttributeStack [StackPointer + i]
  1214.  
  1215.  
  1216.  
  1217. The action statement can compute an attribute value for the left-hand side  of
  1218.  
  1219.  
  1220.  
  1221.  
  1222.  
  1223.  
  1224.  
  1225.  
  1226.  
  1227.  
  1228.  
  1229.  
  1230.  
  1231.  
  1232.                                                                             17
  1233.  
  1234.  
  1235. the  production  which  has to be assigned to the variable SynAttribute (for a
  1236.  
  1237.  
  1238. synthesized attribute). After executing the action statements, this  value  is
  1239.  
  1240.  
  1241. pushed  onto the attribute stack by the parser to reestablish the invariant of
  1242.  
  1243.  
  1244. the algorithm. The attribute values for terminals have to be provided  by  the
  1245.  
  1246.  
  1247. scanner.
  1248.  
  1249.  
  1250.  
  1251.      As many of the terminals don't bear any attribute, most of the associated
  1252.  
  1253.  
  1254. Push operations could be replaced by pushing a dummy value: that is, an incre-
  1255.  
  1256.  
  1257. ment of the stack pointer would be enough. To be able to  distinguish  between
  1258.  
  1259.  
  1260. two kinds of Push operations, two kinds of read actions would be necessary. To
  1261.  
  1262.  
  1263. make the decision would cost an extra check for every token.  We did  not  use
  1264.  
  1265.  
  1266. this optimization because we believe that the extra checks cost as much as the
  1267.  
  1268.  
  1269. saved assignments.
  1270.  
  1271.  
  1272.  
  1273.      How do we implement the mapping of a production to the associated  action
  1274.  
  1275.  
  1276. statements? Of course, the natural solution is a CASE statement. The access to
  1277.  
  1278.  
  1279. the Length and the LeftHandSide also depends on  the  production.  One  choice
  1280.  
  1281.  
  1282. would be to access two arrays. As array access is relatively expensive, we can
  1283.  
  1284.  
  1285. move these computations into the CASE statement which we already need for  the
  1286.  
  1287.  
  1288.  
  1289.  
  1290.  
  1291.  
  1292.  
  1293.  
  1294.  
  1295.  
  1296.  
  1297.  
  1298.                                                                             18
  1299.  
  1300.  
  1301. action statements. In each case, these computations are turned into constants.
  1302.  
  1303.  
  1304. The FOR loop disappears anyway, because it suffices  to  decrement  the  stack
  1305.  
  1306.  
  1307. pointers. The code common to all reductions is not included in the CASE state-
  1308.  
  1309.  
  1310. ment but follows afterwards.  We also factor out the code common to all  parts
  1311.  
  1312.  
  1313. of the IF statement at the end of each reduction (Algorithm 5).
  1314.  
  1315.  
  1316.  
  1317.      Parts of the code in the case alternatives can be evaluated  during  gen-
  1318.  
  1319.  
  1320. eration  time.   In  Algorithm  5,  p  and  q stand for arbitrary productions.
  1321.  
  1322.  
  1323. Whereas in the case of p the code has just been carried over from Algorithm 4,
  1324.  
  1325.  
  1326. the  case of q contains the code after applying constant folding: the FOR loop
  1327.  
  1328.  
  1329. reduces to a decrement of the stack pointers and  the  precomputation  of  the
  1330.  
  1331.  
  1332. left-hand side of q yields a constant:
  1333.  
  1334.  
  1335.  
  1336.     m = Length (q)
  1337.  
  1338.  
  1339.     n = LeftHandSide (q)
  1340.  
  1341.  
  1342.  
  1343.  
  1344.      The two stacks could use one common stack pointer if this were  an  array
  1345.  
  1346.  
  1347. index. As we want to arrive at real pointers in a C implementation, we have to
  1348.  
  1349.  
  1350. distinguish between the two stack pointers.
  1351.  
  1352.  
  1353.  
  1354.  
  1355.  
  1356.  
  1357.  
  1358.  
  1359.  
  1360.  
  1361.  
  1362.  
  1363.  
  1364.                                                                             19
  1365.  
  1366. Algorithm 5: LR parser with CASE statement
  1367.  
  1368. BEGIN
  1369.    Push (AttributeStack, _)
  1370.    Push (StateStack, StartState)
  1371.    Terminal := GetToken ()
  1372.    LOOP
  1373.       State := Table (Top (StateStack), Terminal)
  1374.       IF State >= FirstReadReduce THEN          /* reduce or read-reduce? */
  1375.          IF State < FirstReduce THEN            /* read-reduce */
  1376.             Push (AttributeStack, GetAttribute ())
  1377.             Push (StateStack, _)
  1378.             Terminal := GetToken ()
  1379.          END
  1380.          LOOP                                   /* reduce */
  1381.             CASE State OF
  1382.             Stop:       HALT
  1383.             ...
  1384.             p+FirstReadReduce-1, p+FirstReduce-1:
  1385.                FOR i := 1 TO Length (p) DO
  1386.                   Dummy := Pop (AttributeStack)
  1387.                   State := Pop (StateStack)
  1388.                END
  1389.                Nonterminal := LeftHandSide (p)
  1390.                Semantics (p) ()
  1391.             q+FirstReadReduce-1, q+FirstReduce-1:
  1392.                AttributeStackPointer -:= m
  1393.                StateStackPointer     -:= m
  1394.                Nonterminal            := n
  1395.                <action statements for q>        /* Semantics (q) */
  1396.             ...
  1397.             END
  1398.             State := Table (Top (StateStack), Nonterminal)
  1399.             Push (AttributeStack, SynAttribute)
  1400.             Push (StateStack, State)
  1401.             IF State < FirstReadReduce THEN EXIT END /* read */
  1402.          END
  1403.       ELSE                                      /* read */
  1404.          Push (AttributeStack, GetAttribute ())
  1405.          Push (StateStack, State)
  1406.          Terminal := GetToken ()
  1407.       END
  1408.  
  1409.  
  1410.  
  1411.  
  1412.  
  1413.  
  1414.  
  1415.  
  1416.  
  1417.  
  1418.  
  1419.  
  1420.  
  1421.  
  1422.                                                                             20
  1423.    END
  1424. END
  1425.  
  1426.  
  1427.  
  1428.      The halt action (Stop) can be treated as a special case of  a  reduction.
  1429.  
  1430.  
  1431. It  occurs  when the production S' -> S # is recognized. We have augmented the
  1432.  
  1433.  
  1434. given grammar by this production where
  1435.  
  1436.  
  1437.  
  1438.     S   is the original start symbol
  1439.  
  1440.  
  1441.     S'  is the new start symbol
  1442.  
  1443.  
  1444.     #   is the end of input token
  1445.  
  1446.  
  1447.  
  1448. By this we assure that the complete input is parsed and checked for  syntacti-
  1449.  
  1450.  
  1451. cal correctness.
  1452.  
  1453.  
  1454.  
  1455.  
  1456.  
  1457.  
  1458. Table Representation and Access
  1459.  
  1460.  
  1461.  
  1462.      After developing the principle algorithm for LR-parsing, the question  of
  1463.  
  1464.  
  1465. how  to implement the function Table has to be discussed before we turn to the
  1466.  
  1467.  
  1468. details of the implementation. The most natural solution might  be  to  use  a
  1469.  
  1470.  
  1471. two-dimensional  array.  For  large languages like Ada this array would become
  1472.  
  1473.  
  1474. quite big:
  1475.  
  1476.  
  1477.  
  1478.  
  1479.  
  1480.  
  1481.  
  1482.  
  1483.  
  1484.  
  1485.  
  1486.  
  1487.                                                                             21
  1488.  
  1489.  
  1490.     ( 95 terminals + 252 nonterminals ) * 540 states * 2 bytes = 374,760 bytes
  1491.  
  1492.  
  1493.  
  1494. This amount may be bearable with todays main memory  capacities.  However,  we
  1495.  
  1496.  
  1497. have  chosen  the  classical solution of compressing the sparse matrix.  Using
  1498.  
  1499.  
  1500. this compression the storage required for the Ada parser is reduced to  22,584
  1501.  
  1502.  
  1503. bytes,  including  additional  information for error recovery. The decision of
  1504.  
  1505.  
  1506. how to compress the table has to take into account the desired storage  reduc-
  1507.  
  1508.  
  1509. tion and the cost of accessing the compressed data structure.
  1510.  
  1511.  
  1512.  
  1513.      An advantageous compression method is the comb-vector technique described
  1514.  
  1515.  
  1516. in [ASU86] which is used for example in Yacc [Joh75] and in the latest version
  1517.  
  1518.  
  1519. of PGS [KlM89].  This technique combines very good  compression  quality  with
  1520.  
  1521.  
  1522. fast  access.  The rows (or columns) of a sparse matrix are merged into a sin-
  1523.  
  1524.  
  1525. gle vector called Next. Two additional  vectors  called  Base  and  Check  are
  1526.  
  1527.  
  1528. necessary  to  accomplish  the  access to the original data. Base contains for
  1529.  
  1530.  
  1531. every row (column) the offset where it starts  in  Next.  Check  contains  for
  1532.  
  1533.  
  1534. every entry in Next the original row (column) index. The resulting data struc-
  1535.  
  1536.  
  1537. ture resembles the merging of several combs into one. The optional combination
  1538.  
  1539.  
  1540. with a fourth array called Default allows further compression of the array, as
  1541.  
  1542.  
  1543.  
  1544.  
  1545.  
  1546.  
  1547.  
  1548.  
  1549.  
  1550.  
  1551.  
  1552.  
  1553.                                                                             22
  1554.  
  1555.  
  1556. parts common to more than one row (column) can be factored out.   Algorithm  6
  1557.  
  1558.  
  1559. shows how to access the compressed data structure if rows are merged. For more
  1560.  
  1561.  
  1562. details see [ASU86].
  1563.  
  1564. Algorithm 6: access to the compressed table (comb-vector)
  1565.  
  1566. PROCEDURE Table (State, Symbol)
  1567.    LOOP
  1568.       IF Check [Base [State] + Symbol] = State THEN
  1569.          RETURN Next [Base [State] + Symbol]
  1570.       ELSE
  1571.          State := Default [State]
  1572.          IF State = NoState THEN RETURN error END
  1573.       END
  1574.    END
  1575. END
  1576.  
  1577.  
  1578.  
  1579.  
  1580.      With this kind of compression it is possible to reduce the table size  to
  1581.  
  1582.  
  1583. less  than 10%. The space and time behaviour can be improved even more, yield-
  1584.  
  1585.  
  1586. ing in the case of Ada a size reduction of 6%.  Algorithm  6  may  return  the
  1587.  
  1588.  
  1589. value  "error".  However,  if Symbol is a nonterminal, no errors can occur, as
  1590.  
  1591.  
  1592. nonterminals are computed during reductions and are always correct. Therefore,
  1593.  
  1594.  
  1595. in  the case of nonterminal access, if Default is omitted the vector Check can
  1596.  
  1597.  
  1598. be omitted, too. In order to implement this it is necessary to split the func-
  1599.  
  1600.  
  1601. tion Table in two parts, one for terminal and one for nonterminal access:
  1602.  
  1603.  
  1604.  
  1605.  
  1606.  
  1607.  
  1608.  
  1609.  
  1610.  
  1611.  
  1612.  
  1613.  
  1614.  
  1615.  
  1616.  
  1617.  
  1618.                                                                             23
  1619.  
  1620.  
  1621.  
  1622.     TTable    : States x Terminals      -> INTEGER
  1623.  
  1624.  
  1625.     NTTable   : States x Nonterminals   -> INTEGER
  1626.  
  1627.  
  1628.  
  1629.  
  1630.  
  1631.      The terminal Table TTable is accessed as before using Algorithm  6.   The
  1632.  
  1633.  
  1634. access  to  the  nonterminal Table NTTable can be simplified as shown in Algo-
  1635.  
  1636.  
  1637. rithm 7. Only the vectors NTNext and NTBase are necessary in this case.
  1638.  
  1639. Algorithm 7: access to the nonterminal table
  1640.  
  1641. PROCEDURE NTTable (State, Symbol)
  1642.    RETURN NTNext [NTBase [State] + Symbol]
  1643. END
  1644.  
  1645.  
  1646.  
  1647.  
  1648.      Splitting the table into two parts has several  advantages:  the  storage
  1649.  
  1650.  
  1651. reduction  is  improved  because the two parts pack better if handled indepen-
  1652.  
  1653.  
  1654. dently. The storage reduction by omitting Default and Check  is  greater  than
  1655.  
  1656.  
  1657. using  these  two  vectors.  The  table access time in the nonterminal case is
  1658.  
  1659.  
  1660. improved significantly.
  1661.  
  1662.  
  1663.  
  1664.      A further possibility to reduce space is to  make  the  arrays  Next  and
  1665.  
  1666.  
  1667. Check only as large as the significant entries require. Then a check has to be
  1668.  
  1669.  
  1670. inserted to see if the expression 'Base [State] + Symbol' is within the  array
  1671.  
  1672.  
  1673.  
  1674.  
  1675.  
  1676.  
  1677.  
  1678.  
  1679.  
  1680.  
  1681.  
  1682.  
  1683.                                                                             24
  1684.  
  1685.  
  1686. bounds. We decided to save this check by increasing the arrays to a size where
  1687.  
  1688.  
  1689. no bounds violations can occur.
  1690.  
  1691.  
  1692.  
  1693.  
  1694.  
  1695.  
  1696. Error Recovery
  1697.  
  1698.  
  1699.  
  1700.      The error recovery of Lalr follows  the  complete  backtrack-free  method
  1701.  
  1702.  
  1703. described  by [Roeh76,Roeh80,Roeh82].  The error recovery used by Lalr is com-
  1704.  
  1705.  
  1706. pletely automatic and includes error reporting, recovery,  and  repair.   From
  1707.  
  1708.  
  1709. the user's point of view it works as described in a later section.
  1710.  
  1711.  
  1712.  
  1713.      There is only one place where an error can occur: in  an  access  to  the
  1714.  
  1715.  
  1716. terminal  table  where  the lookahead token is not a legal continuation of the
  1717.  
  1718.  
  1719. program recognized so far. The algorithm for error recovery  replaces  in  the
  1720.  
  1721.  
  1722. access  of  the  terminal table (Algorithm 6) the return of the value "error".
  1723.  
  1724.  
  1725. The parsing algorithm has two modes: the regular mode and the repair mode used
  1726.  
  1727.  
  1728. during  error  repair. A problem is that during repair mode reductions and the
  1729.  
  1730.  
  1731. associated semantic actions have to be executed. To avoid duplication of  this
  1732.  
  1733.  
  1734. code  we  use the same code during both modes. In order to avoid the immediate
  1735.  
  1736.  
  1737.  
  1738.  
  1739.  
  1740.  
  1741.  
  1742.  
  1743.  
  1744.  
  1745.  
  1746.  
  1747.  
  1748.                                                                             25
  1749.  
  1750.  
  1751. generation of new errors in repair mode error messages and skipping of  tokens
  1752.  
  1753.  
  1754. are disabled in this mode. Repair mode is exited when we can accept a token at
  1755.  
  1756.  
  1757. one of the two places where tokens are read.  We add an instruction  at  these
  1758.  
  1759.  
  1760. places to leave repair mode.
  1761.  
  1762.  
  1763.  
  1764.      It might be argued that this additional instruction to leave repair  mode
  1765.  
  1766.  
  1767. could be avoided. This is true, if either the code for reductions and semantic
  1768.  
  1769.  
  1770. actions were duplicated or turned into  a  procedure  which  could  be  called
  1771.  
  1772.  
  1773. twice. The first solution would increase the code size significantly.  This is
  1774.  
  1775.  
  1776. avoided in the second solution, but we have to pay for  a  procedure  call  at
  1777.  
  1778.  
  1779. every reduction. This is more expensive than a simple assignment to change the
  1780.  
  1781.  
  1782. mode for every input token, because the number  of  input  tokens  is  usually
  1783.  
  1784.  
  1785. about the same as the number of reductions.
  1786.  
  1787.  
  1788.  
  1789.  
  1790.  
  1791.  
  1792. Mapping Pseudo Code to C
  1793.  
  1794.  
  1795.  
  1796.      The remaining implementation decisions are how to  map  the  pseudo  code
  1797.  
  1798.  
  1799. instructions   into   real  programming  language  statements.  This  concerns
  1800.  
  1801.  
  1802.  
  1803.  
  1804.  
  1805.  
  1806.  
  1807.  
  1808.  
  1809.  
  1810.  
  1811.  
  1812.  
  1813.                                                                             26
  1814.  
  1815.  
  1816. primarily the operations Push and Top and the  table  access.   The  following
  1817.  
  1818.  
  1819. arguments hold only for the language C, as things are different in Modula-2.
  1820.  
  1821.  
  1822.  
  1823.      The communication of the attribute value of a token from the  scanner  is
  1824.  
  1825.  
  1826. not done by the procedure GetAttribute but by the global variable Attribute.
  1827.  
  1828.  
  1829.  
  1830.      Stacks are implemented as  arrays  which  are  administered  by  a  stack
  1831.  
  1832.  
  1833. pointer.   If  the stack pointer is a register variable a Push operation could
  1834.  
  1835.  
  1836. be accomplished by one machine instruction on an appropriate machine  if  auto
  1837.  
  1838.  
  1839. increment is used. We preferred post increment, because we use a MC 68020 pro-
  1840.  
  1841.  
  1842. cessor.
  1843.  
  1844.     Push (AttributeStack, Attribute)   =>   * yyAttrStackPtr ++ = Attribute;
  1845.  
  1846.  
  1847.  
  1848.  
  1849.      The operation Top turns into dereferencing a pointer.
  1850.  
  1851.     Top (StateStack)   =>   * yyStateStackPtr
  1852.  
  1853.  
  1854.  
  1855.  
  1856.      A dummy value is easily pushed by an increment instruction.
  1857.  
  1858.     Push (StateStack, _)   =>   yyStateStackPtr ++;
  1859.  
  1860.  
  1861.  
  1862.  
  1863.      The vector NTBase does not contain integer  values  but  direct  pointers
  1864.  
  1865.  
  1866. into  the  vector  NTNext  to  indicate where the rows start in NTNext.  These
  1867.  
  1868.  
  1869.  
  1870.  
  1871.  
  1872.  
  1873.  
  1874.  
  1875.  
  1876.  
  1877.  
  1878.  
  1879.                                                                             27
  1880.  
  1881.  
  1882. pointers are initialized after loading of the program. This saves the addition
  1883.  
  1884.  
  1885. of  the  start  address  of  NTNext  during the outer array access.  The start
  1886.  
  1887.  
  1888. address is already preadded to the elements of the vector NTBase.   This  kind
  1889.  
  1890.  
  1891. of  access  is  probably  cheaper  than  the  access  to  an uncompressed two-
  1892.  
  1893.  
  1894. dimensional array which usually involves multiplication.  The  same  technique
  1895.  
  1896.  
  1897. is applied to the vector Base of the terminal table.
  1898.  
  1899.     State := NTNext [NTBase [Top (StateStack ())] + Symbol]   =>
  1900.     yyState = * (yyNTBasePtr [* yyStateStackPtr] + yyNonterminal);
  1901.  
  1902.  
  1903.  
  1904.  
  1905.      The terminal table access is handled as follows.  Instead of implementing
  1906.  
  1907.  
  1908. the  vectors  Next and Check as separate arrays, we used one array of records.
  1909.  
  1910.  
  1911. The array is called Comb and the records have  two  fields  called  Check  and
  1912.  
  1913.  
  1914. Next.  This  transformation  saves  one array access, because whenever we have
  1915.  
  1916.  
  1917. computed the address of the Check field we find the Next field besides it.
  1918.  
  1919.     LOOP
  1920.        IF Check [Base [State] + Symbol] = State THEN
  1921.           RETURN Next [Base [State] + Symbol]
  1922.        ELSE
  1923.           State := Default [State]
  1924.           IF State = NoState THEN <error recovery> END
  1925.        END
  1926.     END
  1927.     =>
  1928.  
  1929.  
  1930.  
  1931.  
  1932.  
  1933.  
  1934.  
  1935.  
  1936.  
  1937.  
  1938.  
  1939.  
  1940.  
  1941.  
  1942.  
  1943.  
  1944.                                                                             28
  1945.     for (;;) {
  1946.        typedef struct { int Check, Next; } yyTCombType;
  1947.        register yyTCombType * TCombPtr;
  1948.        TCombPtr = yyTBasePtr [yyState] + yyTerminal;
  1949.        if (TCombPtr->Check == yyState) {
  1950.           yyState = TCombPtr->Next;
  1951.           break;
  1952.        };
  1953.        if ((yyState = yyDefault [yyState]) != yyNoState) continue;
  1954.        < code for error recovery >
  1955.     }
  1956.  
  1957.  
  1958.  
  1959.  
  1960.      To check for stack overflow we have to add the code below at  every  read
  1961.  
  1962.  
  1963. (terminal) action. Read-reduce and reduce actions can only cause the stacks to
  1964.  
  1965.  
  1966. grow by at most one element and therefore don't need an  overflow  check.   We
  1967.  
  1968.  
  1969. have  implemented the stacks as flexible arrays. In case of stack overflow the
  1970.  
  1971.  
  1972. sizes of the arrays are increased  automatically.  Therefore  from  the  users
  1973.  
  1974.  
  1975. point of view stack overflow never occurs.
  1976.  
  1977.     if (yyStateStackPtr >= yyMaxPtr) < allocate larger arrays and copy the contents >
  1978.  
  1979.  
  1980.  
  1981.  
  1982.      Of course we have used the register attribute for  the  most  used  vari-
  1983.  
  1984.  
  1985. ables:
  1986.  
  1987.     yyState, yyTerminal, yyNonterminal, yyStateStackPtr, yyAttrStackPtr, TCombPtr
  1988.  
  1989.  
  1990.  
  1991.  
  1992.      The variables yyTerminal and yyNonterminal have  the  type  long  because
  1993.  
  1994.  
  1995. they  are  involved  in  address computations. These have to be performed with
  1996.  
  1997.  
  1998.  
  1999.  
  2000.  
  2001.  
  2002.  
  2003.  
  2004.  
  2005.  
  2006.  
  2007.  
  2008.  
  2009.                                                                             29
  2010.  
  2011.  
  2012. long arithmetic in C. The long declaration saves explicit machine instructions
  2013.  
  2014.  
  2015. for length conversions (at least on MC 68020 processors). The variable yyState
  2016.  
  2017.  
  2018. has the type short in order to be compatible with the type of the  table  ele-
  2019.  
  2020.  
  2021. ments. These have the type short to keep the table size reasonable small.
  2022.  
  2023.  
  2024.  
  2025.      Continue statements have been changed to  explicit  gotos,  because  some
  2026.  
  2027.  
  2028. compilers don't generate optimal jump instructions.
  2029.  
  2030.  
  2031.  
  2032.      The appendix shows an example of a parser generated by  Lalr.  The  error
  2033.  
  2034.  
  2035. recovery,  which  constitutes most of the code, and some other parts have been
  2036.  
  2037.  
  2038. omitted.  Some details may deviate from the above  description  which  concen-
  2039.  
  2040.  
  2041. trated primarily on the principles. For example all identifiers carry the pre-
  2042.  
  2043.  
  2044. fix 'yy' to avoid conflicts with identifiers introduced by the user  into  the
  2045.  
  2046.  
  2047. semantic  actions.   Also,  the  sizes  of  the arrays are greater than really
  2048.  
  2049.  
  2050. necessary as we used a non-dense coding for the terminal symbols.
  2051.  
  2052.  
  2053.  
  2054.  
  2055.  
  2056.  
  2057.                              THE PARSER GENERATOR
  2058.  
  2059.  
  2060.  
  2061.  
  2062.  
  2063.  
  2064.  
  2065.  
  2066.  
  2067.  
  2068.  
  2069.  
  2070.  
  2071.  
  2072.  
  2073.                                                                             30
  2074.  
  2075.  
  2076.      This chapter will discuss important features of the generator  Lalr  from
  2077.  
  2078.  
  2079. the user's point of view.
  2080.  
  2081.  
  2082.  
  2083.  
  2084.  
  2085.  
  2086. Structure of Specification
  2087.  
  2088.  
  2089.  
  2090.      The structure of a parser specification is shown in Figure 1.  There  may
  2091.  
  2092.  
  2093. be  five sections to include arbitrary target code, which may contain declara-
  2094.  
  2095.  
  2096. tions to be used in the semantic actions or statements for initialization  and
  2097.  
  2098.  
  2099. finalization  of  data structures.  The TOKEN section defines the terminals of
  2100.  
  2101.  
  2102. the grammar and their encoding. In the OPER (for operator) section, precedence
  2103.  
  2104.  
  2105. and  associativity for terminals can be specified to resolve LR-conflicts. The
  2106.  
  2107.  
  2108. RULE section contains the grammar rules  and  semantic  actions.   A  complete
  2109.  
  2110.  
  2111. definition  of  the  specification  language  can  be found in the user manual
  2112.  
  2113.  
  2114. [GrV88].
  2115.  
  2116.  
  2117.  
  2118.  
  2119.  
  2120.  
  2121.  
  2122.  
  2123.  
  2124.  
  2125.  
  2126.  
  2127.  
  2128.  
  2129.  
  2130.  
  2131.  
  2132.  
  2133.  
  2134.  
  2135.  
  2136.  
  2137.                                                                             31
  2138.     EXPORT { external declarations }
  2139.     GLOBAL { global   declarations }
  2140.     LOCAL  { local    declarations }
  2141.     BEGIN  { initialization code   }
  2142.     CLOSE  { finalization   code   }
  2143.     TOKEN    coding of terminals
  2144.     OPER     precedence of operators
  2145.     RULE     grammar rules and semantic actions
  2146.  
  2147.  
  2148.  
  2149.     Fig. 1: Structure of Specification
  2150.  
  2151.  
  2152.  
  2153.  
  2154.  
  2155.  
  2156.  
  2157. Semantic Actions and S-Attribution
  2158.  
  2159.  
  2160.  
  2161.      A parser for practical applications has  more  to  do  than  just  syntax
  2162.  
  2163.  
  2164. checking:  it  must allow some kind of translation of the recognized input. In
  2165.  
  2166.  
  2167. the case of LR parsing, action statements can be associated with  the  produc-
  2168.  
  2169.  
  2170. tions.  These statements are executed whenever the production is reduced.
  2171.  
  2172.  
  2173.  
  2174.      Additionally, during  LR  parsing  it  is  possible  to  evaluate  an  S-
  2175.  
  2176.  
  2177. attribution  (only  synthesized  attributes). This mechanism works as follows:
  2178.  
  2179.  
  2180. every terminal or nonterminal of the grammar can be associated with an  attri-
  2181.  
  2182.  
  2183. bute.  After  a  reduction  (that is during the execution of the action state-
  2184.  
  2185.  
  2186. ments) the attributes of the symbols on the right-hand  side  of  the  reduced
  2187.  
  2188.  
  2189.  
  2190.  
  2191.  
  2192.  
  2193.  
  2194.  
  2195.  
  2196.  
  2197.  
  2198.  
  2199.  
  2200.  
  2201.                                                                             32
  2202.  
  2203.  
  2204. production can be accessed from an attribute stack using the notation $i.  The
  2205.  
  2206.  
  2207. action statement can compute an attribute value for the left-hand side of  the
  2208.  
  2209.  
  2210. production which has to be assigned to the special variable $$.  After execut-
  2211.  
  2212.  
  2213. ing the action statements, this value is pushed onto the  attribute  stack  by
  2214.  
  2215.  
  2216. the parser to reestablish the invariant of the algorithm. The attribute values
  2217.  
  2218.  
  2219. for terminals have to be provided by the scanner.  Figure 2 shows  an  example
  2220.  
  2221.  
  2222. for the syntax of grammar rules, semantic actions, and S-attribution.
  2223.  
  2224.     expr : expr '+' expr { $$.value := $1.value + $3.value; } .
  2225.     expr : expr '*' expr { $$.value := $1.value * $3.value; } .
  2226.     expr : '(' expr ')'  { $$.value := $2.value; } .
  2227.     expr : number        { $$.value := $1.value; } .
  2228.  
  2229.  
  2230.  
  2231.     Fig. 2: Grammar Rules with Semantic Actions and S-Attribution
  2232.  
  2233.  
  2234.  
  2235.  
  2236.  
  2237.  
  2238.  
  2239. Ambiguous Grammars
  2240.  
  2241.  
  2242.  
  2243.      The grammar of Figure 2 as well as the example in Figure  3  are  typical
  2244.  
  2245.  
  2246. examples  of  ambiguous grammars. Like Yacc we allow resulting LR-conflicts to
  2247.  
  2248.  
  2249. be resolved by specifying precedence and associativity for  terminals  in  the
  2250.  
  2251.  
  2252. OPER section. Figure 4 gives an example. The lines represent increasing levels
  2253.  
  2254.  
  2255.  
  2256.  
  2257.  
  2258.  
  2259.  
  2260.  
  2261.  
  2262.  
  2263.  
  2264.  
  2265.                                                                             33
  2266.  
  2267.  
  2268. of  precedence.  LEFT,  RIGHT,  and  NONE  denote  left-associativity,  right-
  2269.  
  2270.  
  2271. associativity,  and  no  associativity.  Rules can inherit the properties of a
  2272.  
  2273.  
  2274. terminal with the PREC suffix.
  2275.  
  2276.     stmt : IF expr THEN stmt             PREC LOW
  2277.          | IF expr THEN stmt ELSE stmt   PREC HIGH .
  2278.  
  2279.  
  2280.  
  2281.     Fig. 3: Ambiguous Grammar (Dangling Else)
  2282.  
  2283.  
  2284.     OPER  LEFT '+'
  2285.           LEFT '*'
  2286.           NONE LOW
  2287.           NONE HIGH
  2288.  
  2289.  
  2290.  
  2291.     Fig. 4: Resolution of LR-Conflicts Using Precedence and Associativity
  2292.  
  2293.  
  2294.  
  2295.  
  2296.  
  2297.  
  2298.  
  2299. LR-Conflict Message
  2300.  
  2301.  
  2302.  
  2303.      To help a user locate the reason for LR-conflicts, we adopted the  method
  2304.  
  2305.  
  2306. proposed  by  [DeP82].  Besides  reporting  the  type  of the conflict and the
  2307.  
  2308.  
  2309. involved items (whatever that is for the user) like most LR parser  generators
  2310.  
  2311.  
  2312. do,  the  system also prints a derivation tree.  Figure 5 shows an example. It
  2313.  
  2314.  
  2315. shows how the items and the look-ahead tokens get into the conflict situation.
  2316.  
  2317.  
  2318.  
  2319.  
  2320.  
  2321.  
  2322.  
  2323.  
  2324.  
  2325.  
  2326.  
  2327.  
  2328.  
  2329.                                                                             34
  2330.  
  2331.     State 266
  2332.     read reduce conflict
  2333.     program End-of-Tokens
  2334.     PROGRAM identifier params ';' block '.'
  2335.     ..............................:
  2336.     :
  2337.     labels consts types vars procs BEGIN stmts END
  2338.     .....................................:
  2339.     :
  2340.     stmt
  2341.     IF expr THEN stmt ELSE stmt
  2342.                    :
  2343.                    IF expr THEN stmt
  2344.                    :
  2345.     reduce stmt -> IF expr THEN stmt. {ELSE} ?
  2346.     read   stmt -> IF expr THEN stmt.ELSE stmt ?
  2347.  
  2348.  
  2349.  
  2350.     Fig. 5: Derivation Tree for an LR-Conflict (Dangling Else)
  2351.  
  2352.  
  2353.  
  2354. In general there can be two trees if the derivations for the conflicting items
  2355.  
  2356.  
  2357. are  different.  Each  tree consists of three parts. An initial part begins at
  2358.  
  2359.  
  2360. the start symbol of the grammar. At a certain node (rule) two subtrees explain
  2361.  
  2362.  
  2363. the emergence of the item and the look-ahead.
  2364.  
  2365.  
  2366.  
  2367.      Every line contains a right-hand side of  a  grammar  rule.  Usually  the
  2368.  
  2369.  
  2370. right-hand  side  is  indented to start below the nonterminal of the left-hand
  2371.  
  2372.  
  2373. side. To avoid line overflow, dotted edges also refer to  the  left-hand  side
  2374.  
  2375.  
  2376. nonterminal and allow a shift back to the left margin. In Figure 5 the initial
  2377.  
  2378.  
  2379. tree part consists of 5 lines (not counting the  dotted  lines).  The  symbols
  2380.  
  2381.  
  2382.  
  2383.  
  2384.  
  2385.  
  2386.  
  2387.  
  2388.  
  2389.  
  2390.  
  2391.  
  2392.  
  2393.  
  2394.                                                                             35
  2395.  
  2396.  
  2397. stmt  and  ELSE  are  the  roots of the other two tree parts. This location is
  2398.  
  2399.  
  2400. indicated by the "unnecessary" colon in the following line.  After one  inter-
  2401.  
  2402.  
  2403. mediate  line  the left subtree derives the conflicting items.  The right sub-
  2404.  
  2405.  
  2406. tree consists in this case only of the root node (the terminal ELSE)  indicat-
  2407.  
  2408.  
  2409. ing  the  look-ahead. In general this can be a tree of arbitrary size. The LR-
  2410.  
  2411.  
  2412. conflict can easily be seen from this tree fragment.   If  conditional  state-
  2413.  
  2414.  
  2415. ments  are  nested as shown, then there is a read reduce conflict (also called
  2416.  
  2417.  
  2418. shift reduce conflict).
  2419.  
  2420.  
  2421.  
  2422.  
  2423.  
  2424.  
  2425. Error Recovery
  2426.  
  2427.  
  2428.  
  2429.      The generated parsers include information and algorithms to handle syntax
  2430.  
  2431.  
  2432. errors completely automatically.  We follow the complete backtrack-free method
  2433.  
  2434.  
  2435. described  by  [Roeh76,Roeh80,Roeh82]  and   provide   expressive   reporting,
  2436.  
  2437.  
  2438. recovery,  and repair. Every incorrect input is "virtually" transformed into a
  2439.  
  2440.  
  2441. syntactically correct  program  with  the  consequence  of  only  executing  a
  2442.  
  2443.  
  2444. "correct"  sequence  of  semantic  actions.   Therefore the following compiler
  2445.  
  2446.  
  2447.  
  2448.  
  2449.  
  2450.  
  2451.  
  2452.  
  2453.  
  2454.  
  2455.  
  2456.  
  2457.  
  2458.  
  2459.                                                                             36
  2460.  
  2461.     Source Program:
  2462.     program test (output);
  2463.     begin
  2464.        if (a = b] write (a);
  2465.     end.
  2466.  
  2467.  
  2468.     Error Messages:
  2469.  
  2470.     3, 13: Error       syntax error
  2471.     3, 13: Information expected symbols: ')' '*' '+' '-' '/' '<' '<=' '=' '<>'
  2472.                                          '>' '>=' AND DIV IN MOD OR
  2473.     3, 15: Information restart point
  2474.     3, 15: Repair      symbol inserted : ')'
  2475.     3, 15: Repair      symbol inserted : THEN
  2476.  
  2477.  
  2478.  
  2479.     Fig. 6: Example of Automatic Error Messages
  2480.  
  2481.  
  2482.  
  2483. phases like semantic analysis don't have to bother with  syntax  errors.  Lalr
  2484.  
  2485.  
  2486. provides  a prototype error module which prints messages as shown in Figure 6.
  2487.  
  2488.  
  2489. Internally the error recovery works as follows:
  2490.  
  2491.  
  2492.  
  2493. - The location of the syntax error is reported.
  2494.  
  2495.  
  2496.  
  2497. - All the tokens that would be a legal continuation of the  program  are  com-
  2498.  
  2499.  
  2500.   puted and reported.
  2501.  
  2502.  
  2503.  
  2504. - All the tokens that can serve to continue parsing are  computed.  A  minimal
  2505.  
  2506.  
  2507.   sequence of tokens is skipped until one of these tokens is found.
  2508.  
  2509.  
  2510.  
  2511.  
  2512.  
  2513.  
  2514.  
  2515.  
  2516.  
  2517.  
  2518.  
  2519.  
  2520.  
  2521.  
  2522.  
  2523.                                                                             37
  2524.  
  2525.  
  2526. - The recovery location is reported.
  2527.  
  2528.  
  2529.  
  2530. - Parsing continues in the so-called repair mode.  In  this  mode  the  parser
  2531.  
  2532.  
  2533.   behaves  as  usual  except that no tokens are read from the input. Instead a
  2534.  
  2535.  
  2536.   minimal sequence of tokens is synthesized to repair the error.   The  parser
  2537.  
  2538.  
  2539.   stays  in  this mode until the input token can be accepted.  The synthesized
  2540.  
  2541.  
  2542.   tokens are reported. The program can be regarded as repaired, if the skipped
  2543.  
  2544.  
  2545.   tokens  are  replaced  by  the  synthesized ones.  Upon leaving repair mode,
  2546.  
  2547.  
  2548.   parsing continues as usual.
  2549.  
  2550.  
  2551.  
  2552.  
  2553.  
  2554.  
  2555.                         COMPARISON OF PARSER GENERATORS
  2556.  
  2557.  
  2558.  
  2559.      Finally we present a comparison of Lalr with some other parser generators
  2560.  
  2561.  
  2562. that  we  have  access to. We are comparing the features of the generators and
  2563.  
  2564.  
  2565. the performance of the generated parsers. Tables 1 to 4  contain  the  results
  2566.  
  2567.  
  2568. and should be self explanatory.  Besides Lalr we examine:
  2569.  
  2570.  
  2571.  
  2572. - Yacc  well known from UNIX [Joh75]
  2573.  
  2574.  
  2575.  
  2576.  
  2577.  
  2578.  
  2579.  
  2580.  
  2581.  
  2582.  
  2583.  
  2584.  
  2585.  
  2586.  
  2587.                                                                             38
  2588.  
  2589.  
  2590. - Bison public domain remake of Yacc [GNU88]
  2591.  
  2592.  
  2593.  
  2594. - PGS   Parser Generating System also developed at Karlsruhe [GrK86,KlM89]
  2595.  
  2596.  
  2597.  
  2598. - Ell   recursive descent parser generator described in [Gro88].
  2599.  
  2600.  
  2601.  
  2602.  
  2603.      Table 1: Comparison of Specification Techniques for Parser Generators
  2604.  
  2605.  
  2606.  
  2607.   ____________________________________________________________________________
  2608.  
  2609.  
  2610.                             Bison     Yacc      PGS        Lalr      Ell
  2611.  
  2612.   ____________________________________________________________________________
  2613.  
  2614.  
  2615.    specification language   BNF       BNF       EBNF       EBNF      EBNF
  2616.  
  2617.  
  2618.    grammar class            LALR(1)   LALR(1)   LALR(1)    LALR(1)   LL(1)
  2619.  
  2620.  
  2621.                                                 LR(1)
  2622.  
  2623.  
  2624.                                                 SLR(1)
  2625.  
  2626.  
  2627.    semantic actions         yes       yes       yes        yes       yes
  2628.  
  2629.  
  2630.    S-attribution            numeric   numeric   symbolic   numeric   -
  2631.  
  2632.  
  2633.    L-attribution            -         -         -          -         symbolic
  2634.  
  2635.   ____________________________________________________________________________
  2636.  
  2637.  
  2638.  
  2639.  
  2640.  
  2641.  
  2642.  
  2643.  
  2644.  
  2645.  
  2646.  
  2647.  
  2648.  
  2649.  
  2650.  
  2651.  
  2652.  
  2653.  
  2654.  
  2655.  
  2656.  
  2657.  
  2658.  
  2659.  
  2660.  
  2661.  
  2662.  
  2663.  
  2664.  
  2665.                          
  2666.  
  2667.  
  2668.  
  2669.  
  2670.  
  2671.  
  2672.  
  2673.  
  2674.  
  2675.  
  2676.  
  2677.  
  2678.  
  2679.  
  2680.  
  2681.  
  2682.  
  2683.  
  2684.  
  2685.  
  2686.  
  2687.  
  2688.  
  2689.  
  2690.  
  2691.  
  2692.                                                                              
  2693.  
  2694.  
  2695.  
  2696.  
  2697.  
  2698.  
  2699.  
  2700.  
  2701.  
  2702.  
  2703.  
  2704.  
  2705.  
  2706.  
  2707.  
  2708.  
  2709.  
  2710.  
  2711.  
  2712.  
  2713.  
  2714.  
  2715.  
  2716.  
  2717.  
  2718.  
  2719.  
  2720.  
  2721.  
  2722.  
  2723.  
  2724.  
  2725.  
  2726.  
  2727.  
  2728.  
  2729.  
  2730.  
  2731.  
  2732.  
  2733.  
  2734.  
  2735.                                                                             39
  2736.  
  2737.  
  2738.      Lalr, PGS, and Ell accept grammars written in extended BNF  whereas  Yacc
  2739.  
  2740.  
  2741. and  Bison  require grammars in BNF. The tools Lalr, PGS, Yacc, and Bison pro-
  2742.  
  2743.  
  2744. cess the large  class  of  LALR(1)  grammars  but  can  only  evaluate  an  S-
  2745.  
  2746.  
  2747. attribution during parsing.  Ell on the other hand processes the smaller class
  2748.  
  2749.  
  2750. of LL(1) grammars but generates parsers which are 50% faster (see  below)  and
  2751.  
  2752.  
  2753. can evaluate a more powerful L-attribution during parsing.
  2754.  
  2755.  
  2756.  
  2757.  
  2758.  
  2759.  
  2760.  
  2761.  
  2762.  
  2763.  
  2764.  
  2765.  
  2766.  
  2767.  
  2768.  
  2769.  
  2770.  
  2771.  
  2772.  
  2773.  
  2774.  
  2775.  
  2776.  
  2777.  
  2778.  
  2779.  
  2780.  
  2781.  
  2782.  
  2783.  
  2784.  
  2785.  
  2786.  
  2787.  
  2788.  
  2789.  
  2790.  
  2791.  
  2792.  
  2793.  
  2794.  
  2795.  
  2796.  
  2797.  
  2798.  
  2799.  
  2800.                                                                             40
  2801.  
  2802.  
  2803.  
  2804.           Table 2: Features for Grammar Conflicts and Error Recovery
  2805.  
  2806.  
  2807.  
  2808. ________________________________________________________________________________________________________
  2809.  
  2810.  
  2811.                           Bison           Yacc            PGS             Lalr            Ell
  2812.  
  2813. ________________________________________________________________________________________________________
  2814.  
  2815.  
  2816.  conflict message         state,          state,          state,          derivation-     involved
  2817.  
  2818.  
  2819.                             items           items           items           tree            productions
  2820.  
  2821.  
  2822.  conflict solution        precedence      precedence      precedence      precedence      first rule
  2823.  
  2824.  
  2825.                           associativity   associativity   associativity   associativity
  2826.  
  2827.  
  2828.                                                           modification
  2829.  
  2830.  
  2831.  chain rule elimination   -               -               yes             -               -
  2832.  
  2833.  
  2834.  error recovery           by hand         by hand         automatic       automatic       automatic
  2835.  
  2836.  
  2837.  error repair             -               -               yes             yes             yes
  2838.  
  2839. ________________________________________________________________________________________________________
  2840.  
  2841.  
  2842.  
  2843.  
  2844.  
  2845.  
  2846.  
  2847.  
  2848.  
  2849.  
  2850.  
  2851.  
  2852.  
  2853.  
  2854.  
  2855.  
  2856.  
  2857.  
  2858.  
  2859.  
  2860.  
  2861.  
  2862.  
  2863.  
  2864.  
  2865.  
  2866.  
  2867.  
  2868.  
  2869.  
  2870.  
  2871.                        
  2872.  
  2873.  
  2874.  
  2875.  
  2876.  
  2877.  
  2878.  
  2879.  
  2880.  
  2881.  
  2882.  
  2883.  
  2884.  
  2885.  
  2886.  
  2887.  
  2888.  
  2889.  
  2890.  
  2891.  
  2892.  
  2893.  
  2894.  
  2895.  
  2896.  
  2897.  
  2898.  
  2899.  
  2900.  
  2901.                                                                                                        
  2902.  
  2903.  
  2904.  
  2905.  
  2906.  
  2907.  
  2908.  
  2909.  
  2910.  
  2911.  
  2912.  
  2913.  
  2914.  
  2915.  
  2916.  
  2917.  
  2918.  
  2919.  
  2920.  
  2921.  
  2922.  
  2923.  
  2924.  
  2925.  
  2926.  
  2927.  
  2928.  
  2929.  
  2930.  
  2931.  
  2932.  
  2933.  
  2934.  
  2935.  
  2936.  
  2937.      In case of grammar conflicts, Lalr has the advantage of providing deriva-
  2938.  
  2939.  
  2940. tion  trees to support the location and solution of this kind of problems. The
  2941.  
  2942.  
  2943. tools Lalr, PGS, and Ell provide automatic error recovery and repair  in  case
  2944.  
  2945.  
  2946.  
  2947.  
  2948.  
  2949.  
  2950.  
  2951.  
  2952.  
  2953.  
  2954.  
  2955.  
  2956.  
  2957.                                                                             41
  2958.  
  2959.  
  2960. of syntax errors.
  2961.  
  2962.  
  2963.  
  2964.  
  2965.         Table 3: Comparison of Implementation Techniques and Languages
  2966.  
  2967.  
  2968.  
  2969. __________________________________________________________________________________________________
  2970.  
  2971.  
  2972.                             Bison          Yacc           PGS            Lalr           Ell
  2973.  
  2974. __________________________________________________________________________________________________
  2975.  
  2976.  
  2977.  parsing method             table-driven   table-driven   table-driven   table-driven   recursive
  2978.  
  2979.  
  2980.                                                                                           descent
  2981.  
  2982.  
  2983.  table compression          comb-vector    comb-vector    comb-vector    comb-vector    -
  2984.  
  2985. __________________________________________________________________________________________________
  2986.  
  2987.  
  2988.  implementation languages   C              C              Pascal         C              C
  2989.  
  2990.  
  2991.                                                                          Modula         Modula
  2992.  
  2993.  
  2994.  target languages           C              C              C              C              C
  2995.  
  2996.  
  2997.                                                           Modula         Modula         Modula
  2998.  
  2999.  
  3000.                                                           Pascal
  3001.  
  3002.  
  3003.                                                           Ada
  3004.  
  3005. __________________________________________________________________________________________________
  3006.  
  3007.  
  3008.  
  3009.  
  3010.  
  3011.  
  3012.  
  3013.  
  3014.  
  3015.  
  3016.  
  3017.  
  3018.  
  3019.  
  3020.  
  3021.  
  3022.  
  3023.  
  3024.  
  3025.  
  3026.  
  3027.  
  3028.  
  3029.  
  3030.  
  3031.  
  3032.  
  3033.  
  3034.  
  3035.  
  3036.  
  3037.  
  3038.  
  3039.  
  3040.  
  3041.  
  3042.                          
  3043.  
  3044.  
  3045.  
  3046.  
  3047.  
  3048.  
  3049.  
  3050.  
  3051.  
  3052.  
  3053.  
  3054.  
  3055.  
  3056.  
  3057.  
  3058.  
  3059.  
  3060.  
  3061.  
  3062.  
  3063.  
  3064.  
  3065.  
  3066.  
  3067.  
  3068.  
  3069.  
  3070.  
  3071.  
  3072.  
  3073.  
  3074.  
  3075.  
  3076.  
  3077.                                                                                                  
  3078.  
  3079.  
  3080.  
  3081.  
  3082.  
  3083.  
  3084.  
  3085.  
  3086.  
  3087.  
  3088.  
  3089.  
  3090.  
  3091.  
  3092.  
  3093.  
  3094.  
  3095.  
  3096.  
  3097.  
  3098.  
  3099.  
  3100.  
  3101.  
  3102.  
  3103.  
  3104.  
  3105.  
  3106.  
  3107.  
  3108.  
  3109.  
  3110.  
  3111.  
  3112.  
  3113.  
  3114.  
  3115.  
  3116.  
  3117.  
  3118.  
  3119.  
  3120.  
  3121.  
  3122.  
  3123.  
  3124.  
  3125.  
  3126.  
  3127.  
  3128.                                                                             42
  3129.  
  3130.  
  3131.      All the LALR(1) tools generate table-driven parsers  and  use  the  comb-
  3132.  
  3133.  
  3134. vector technique for table compression.  Ell produces directly coded recursive
  3135.  
  3136.  
  3137. descent parsers. Whereas Yacc and Bison are implemented in C  and  generate  C
  3138.  
  3139.  
  3140. code,  the  sources  of  Lalr and Ell exist in Modula-2 and in C and the tools
  3141.  
  3142.  
  3143. generate Modula-2 as well as C.  PGS is implemented in  Pascal  and  generates
  3144.  
  3145.  
  3146. parsers in even more languages.
  3147.  
  3148.  
  3149.  
  3150.      The parser measurements (Table 4) exclude time and size for scanning  and
  3151.  
  3152.  
  3153. refer to experiments with Modula-2 parsers.  The grammar for the LALR(1) tools
  3154.  
  3155.  
  3156. had 69 terminals, 52 nonterminals, and 163 productions. The  grammar  for  Ell
  3157.  
  3158.  
  3159. was  in  extended BNF and contained 69 terminals, 45 nonterminals, and 91 pro-
  3160.  
  3161.  
  3162. ductions. The timings result from analyzing a big Modula-2 program  containing
  3163.  
  3164.  
  3165. 28,302  tokens,  7867 lines, or 193,862 characters with the generated parsers.
  3166.  
  3167.  
  3168. For this input the parser generated from Lalr  executed  13,827  read,  14,476
  3169.  
  3170.  
  3171. read-terminal-reduce,   11,422   read-nonterminal-reduce,  and  18,056  reduce
  3172.  
  3173.  
  3174. actions. The terminal Table TTable was accessed 46,358 times and the nontermi-
  3175.  
  3176.  
  3177. nal Table NTTable 43,953 times.
  3178.  
  3179.  
  3180.  
  3181.  
  3182.  
  3183.  
  3184.  
  3185.  
  3186.  
  3187.  
  3188.  
  3189.  
  3190.  
  3191.  
  3192.  
  3193.                                                                             43
  3194.  
  3195.  
  3196.  
  3197.                 Table 4: Comparison of Parser Speeds and Sizes
  3198.  
  3199.  
  3200.  
  3201.     ______________________________________________________________________
  3202.  
  3203.  
  3204.                                 Bison     Yacc      PGS     Lalr      Ell
  3205.  
  3206.     ______________________________________________________________________
  3207.  
  3208.  
  3209.      speed [103 tokens/sec.]     8.93    15.94    17.32    34.94    54.64
  3210.  
  3211.  
  3212.      speed [103 lines/min.]       150      270      290      580      910
  3213.  
  3214.     ______________________________________________________________________
  3215.  
  3216.  
  3217.      table size [bytes]         7,724    9,968    9,832    9,620        -
  3218.  
  3219.  
  3220.      parser size [bytes]       10,900   12,200   14,140   16,492   18,048
  3221.  
  3222.     ______________________________________________________________________
  3223.  
  3224.  
  3225.      generation time [sec.]      4.92    17.28    51.04    27.46    10.48
  3226.  
  3227.     ______________________________________________________________________
  3228.  
  3229.  
  3230.    
  3231.  
  3232.  
  3233.  
  3234.  
  3235.  
  3236.  
  3237.  
  3238.  
  3239.  
  3240.  
  3241.  
  3242.  
  3243.  
  3244.  
  3245.  
  3246.  
  3247.  
  3248.  
  3249.  
  3250.  
  3251.  
  3252.  
  3253.  
  3254.  
  3255.                             
  3256.  
  3257.  
  3258.  
  3259.  
  3260.  
  3261.  
  3262.  
  3263.  
  3264.  
  3265.  
  3266.  
  3267.  
  3268.  
  3269.  
  3270.  
  3271.  
  3272.  
  3273.  
  3274.  
  3275.  
  3276.  
  3277.  
  3278.  
  3279.  
  3280.                                                                          
  3281.  
  3282.  
  3283.  
  3284.  
  3285.  
  3286.  
  3287.  
  3288.  
  3289.  
  3290.  
  3291.  
  3292.  
  3293.  
  3294.  
  3295.  
  3296.  
  3297.  
  3298.  
  3299.  
  3300.  
  3301.  
  3302.  
  3303.  
  3304.  
  3305.  
  3306.  
  3307.  
  3308.  
  3309.  
  3310.  
  3311.      In the presented experiment Lalr generated parsers are twice as  fast  as
  3312.  
  3313.  
  3314. those of Yacc. In general, we observed speed-ups between two and three depend-
  3315.  
  3316.  
  3317. ing on the grammar. The sizes of the parse tables are nearly the same  in  all
  3318.  
  3319.  
  3320. cases.   The  parser  generated  by  Lalr  is 35% larger then the Yacc parser,
  3321.  
  3322.  
  3323. mainly because of the code for  error  recovery.  The  generation  times  vary
  3324.  
  3325.  
  3326.  
  3327.  
  3328.  
  3329.  
  3330.  
  3331.  
  3332.  
  3333.  
  3334.  
  3335.  
  3336.                                                                             44
  3337.  
  3338.  
  3339. widely. The reasons can be found in the different algorithms for computing the
  3340.  
  3341.  
  3342. look-ahead sets and the quality of the implementation.  Lalr spends a  consid-
  3343.  
  3344.  
  3345. erable  amount of time in printing the derivation trees for LR-conflicts.  PGS
  3346.  
  3347.  
  3348. generated parsers turned out to be faster in comparison  to  Yacc,  but  Bison
  3349.  
  3350.  
  3351. performed  considerably  slower. Parsers generated by Ell were found to be the
  3352.  
  3353.  
  3354. fastest exceeding the speed of Lalr by 50%.
  3355.  
  3356.  
  3357.  
  3358.      The measurements of the parser speed turned out to be a  hairy  business.
  3359.  
  3360.  
  3361. The results can be influenced in many ways from:
  3362.  
  3363.  
  3364.  
  3365. - The hardware: we used a PCS Cadmus 9900 with a MC68020 processor running  at
  3366.  
  3367.  
  3368.   a clock rate of 16.7 MHz.
  3369.  
  3370.  
  3371.  
  3372. - The loader: our timings include the time to load the parser which is  almost
  3373.  
  3374.  
  3375.   zero.
  3376.  
  3377.  
  3378.  
  3379. - The compiler: we used the C compiler of PCS.
  3380.  
  3381.  
  3382.  
  3383. - The language: we used Modula-2.
  3384.  
  3385.  
  3386.  
  3387.  
  3388.  
  3389.  
  3390.  
  3391.  
  3392.  
  3393.  
  3394.  
  3395.  
  3396.  
  3397.  
  3398.  
  3399.  
  3400.  
  3401.                                                                             45
  3402.  
  3403.  
  3404. - The size of the language: in the case of Lalr the size of  the  language  or
  3405.  
  3406.  
  3407.   the  size  of the grammar does not influence the speed of the parser because
  3408.  
  3409.  
  3410.   the same table-driven algorithm and the same data structure is used in every
  3411.  
  3412.  
  3413.   case.  This  can  be  different  for other parsers. For example the speed of
  3414.  
  3415.  
  3416.   directly coded parsers decreases with the grammar size.  One reason is  that
  3417.  
  3418.  
  3419.   linear  or  binary-tree search is done to determine the next action. Another
  3420.  
  3421.  
  3422.   reason can be that long jump instructions become necessary instead of  short
  3423.  
  3424.  
  3425.   ones.   PGS  stores states in one byte if there are less than 256 states and
  3426.  
  3427.  
  3428.   in two bytes otherwise. This decreases the speed for large grammars, too, at
  3429.  
  3430.  
  3431.   least on byte-addressable machines.
  3432.  
  3433.  
  3434.  
  3435. - The grammar style, the number of rules, especially chain rules and the like:
  3436.  
  3437.  
  3438.   we used the same grammar except for Ell which had as few chain rules as pos-
  3439.  
  3440.  
  3441.   sible and which caused as few reduce actions as possible. This means  e.  g.
  3442.  
  3443.  
  3444.   we specified expressions in an ambiguous style like shown in Figure 2.
  3445.  
  3446.  
  3447.  
  3448. - The test input: we used the same large Modula program as test data in  every
  3449.  
  3450.  
  3451.   case,  of  course.  Nevertheless the programming style or the code "density"
  3452.  
  3453.  
  3454.   influence the resulting speed when it is measured in lines per minute.
  3455.  
  3456.  
  3457.  
  3458.  
  3459.  
  3460.  
  3461.  
  3462.  
  3463.  
  3464.  
  3465.  
  3466.  
  3467.                                                                             46
  3468.  
  3469.  
  3470. - The timing: we measured CPU-time and subtracted the scanner  time  from  the
  3471.  
  3472.  
  3473.   total  time  (scanner  and  parser) to get the parser time. We used the UNIX
  3474.  
  3475.  
  3476.   shell command time to get the time and added user and system time.
  3477.  
  3478.  
  3479.  
  3480. - The measure: we selected tokens per second as well as lines  per  minute  as
  3481.  
  3482.  
  3483.   measures.  The  first  one  is  independent  of the density of the input and
  3484.  
  3485.  
  3486.   therefore more exact. The second one has been used by other authors  and  it
  3487.  
  3488.  
  3489.   is more expressive for a user.
  3490.  
  3491.  
  3492.  
  3493. - The semantic actions: we specified empty semantic actions for all  rules  in
  3494.  
  3495.  
  3496.   order  to  simulate the conditions in a realistic application. This has more
  3497.  
  3498.  
  3499.   consequences than one might think. It disables a short cut of Yacc  and  the
  3500.  
  3501.  
  3502.   chain rule elimination [WaG84] of PGS, decreasing the speed in both cases.
  3503.  
  3504.  
  3505.  
  3506.      Our conclusion from the numerous problems with the measurement of  parser
  3507.  
  3508.  
  3509. speed is that results from different environments or from different people can
  3510.  
  3511.  
  3512. not be compared. There are too many conditions that influence the results  and
  3513.  
  3514.  
  3515. usually only a few of these conditions are reported.
  3516.  
  3517.  
  3518.  
  3519.  
  3520.  
  3521.  
  3522.  
  3523.  
  3524.  
  3525.  
  3526.  
  3527.  
  3528.  
  3529.  
  3530.  
  3531.  
  3532.                                                                             47
  3533.  
  3534.  
  3535.                                   CONCLUSION
  3536.  
  3537.  
  3538.  
  3539.      We showed that table-driven, portable LR(1) parsers  can  be  implemented
  3540.  
  3541.  
  3542. efficiently  in  C  or  similar  languages.  Following the presented ideas the
  3543.  
  3544.  
  3545. parser generator Lalr generates parsers that are two  to  three  times  faster
  3546.  
  3547.  
  3548. than parsers generated by Yacc. They are vary fast and reach a speed of 35,000
  3549.  
  3550.  
  3551. tokens per second or 580,000 lines per minute.  The generated parsers have all
  3552.  
  3553.  
  3554. the  features  needed  for practical applications such as semantic actions, S-
  3555.  
  3556.  
  3557. attribution, and error recovery.
  3558.  
  3559.  
  3560.  
  3561.      We have shown how to develop an efficient parsing  algorithm  in  a  sys-
  3562.  
  3563.  
  3564. tematic  way.  The  starting point was a basic algorithm from a textbook. In a
  3565.  
  3566.  
  3567. stepwise manner we turned it into a relatively short yet  efficient  algorithm
  3568.  
  3569.  
  3570. mainly using pseudo code. Target code was introduced only in the last step.
  3571.  
  3572.  
  3573.  
  3574.      We presented the main features of the  parser  generator  Lalr  from  the
  3575.  
  3576.  
  3577. user's  point  of view. A valuable feature of Lalr is that it prints a deriva-
  3578.  
  3579.  
  3580. tion tree in case of LR-conflicts to aid the  location  of  the  problem.   We
  3581.  
  3582.  
  3583. finally compared the features and performance of some parser generators.
  3584.  
  3585.  
  3586.  
  3587.  
  3588.  
  3589.  
  3590.  
  3591.  
  3592.  
  3593.  
  3594.  
  3595.  
  3596.  
  3597.                                                                             48
  3598.  
  3599.  
  3600.                                ACKNOWLEDGEMENTS
  3601.  
  3602.  
  3603.  
  3604.      Whereas the author contributed the parser skeletons in  C  and  Modula-2,
  3605.  
  3606.  
  3607. the  generator  program  Lalr was written and debugged by Bertram Vielsack who
  3608.  
  3609.  
  3610. also provided the experimental results.  Valuable suggestions to improve  this
  3611.  
  3612.  
  3613. paper  are due to Kai Koskimies and Nigel Horspool.  Nick Graham deserves many
  3614.  
  3615.  
  3616. thanks for transforming my text into idiomatic English.
  3617.  
  3618.  
  3619.  
  3620.  
  3621.  
  3622.  
  3623.                                   REFERENCES
  3624.  
  3625.  
  3626.  
  3627. [ASU86]
  3628.  
  3629.  
  3630.      A. V. Aho, R. Sethi and J. D. Ullman, Compilers: Principles,  Techniques,
  3631.  
  3632.  
  3633.      and Tools, Addison Wesley, Reading, MA, 1986.
  3634.  
  3635.  
  3636.  
  3637. [Ass88]
  3638.  
  3639.  
  3640.      W. Aszmann, A Short Review of High Speed  Compilation,  LNCS  371,  (Oct.
  3641.  
  3642.  
  3643.      1988), 1-10, Springer Verlag.
  3644.  
  3645.  
  3646.  
  3647.  
  3648.  
  3649.  
  3650.  
  3651.  
  3652.  
  3653.  
  3654.  
  3655.  
  3656.  
  3657.  
  3658.  
  3659.  
  3660.  
  3661.                                                                             49
  3662.  
  3663.  
  3664. [DeP82]
  3665.  
  3666.  
  3667.      F. DeRemer and T. Pennello, Efficient Computation of  LALR(1)  Look-Ahead
  3668.  
  3669.  
  3670.      Sets, ACM Trans. Prog. Lang. and Systems 4, 4 (Oct. 1982), 615-649.
  3671.  
  3672.  
  3673.  
  3674. [GNU88]
  3675.  
  3676.  
  3677.      GNU Project, Bison - Manual Page, Public Domain Software, 1988.
  3678.  
  3679.  
  3680.  
  3681. [GrK86]
  3682.  
  3683.  
  3684.      J.  Grosch  and  E.  Klein,  User  Manual   for   the   PGS-System,   GMD
  3685.  
  3686.  
  3687.      Forschungsstelle an der Universitat Karlsruhe, Aug. 1986.
  3688.  
  3689.  
  3690.  
  3691. [Gro88]
  3692.  
  3693.  
  3694.      J. Grosch, Generators for High-Speed Front-Ends, LNCS 371,  (Oct.  1988),
  3695.  
  3696.  
  3697.      81-92, Springer Verlag.
  3698.  
  3699.  
  3700.  
  3701. [GrV88]
  3702.  
  3703.  
  3704.      J. Grosch and B. Vielsack, The Parser Generators Lalr and  Ell,  Compiler
  3705.  
  3706.  
  3707.      Generation   Report  No.  8,  GMD  Forschungsstelle  an  der  Universitat
  3708.  
  3709.  
  3710.      Karlsruhe, Apr. 1988.
  3711.  
  3712.  
  3713.  
  3714.  
  3715.  
  3716.  
  3717.  
  3718.  
  3719.  
  3720.  
  3721.  
  3722.  
  3723.  
  3724.  
  3725.  
  3726.                                                                             50
  3727.  
  3728.  
  3729. [HoW89]
  3730.  
  3731.  
  3732.      R. N. Horspool and M. Whitney, Even Faster LR Parsing,  Report  Dept.  of
  3733.  
  3734.  
  3735.      Computer  Science-114-IR,  University of Victoria, Department of Computer
  3736.  
  3737.  
  3738.      Science, May 1989.
  3739.  
  3740.  
  3741.  
  3742. [Joh75]
  3743.  
  3744.  
  3745.      S. C. Johnson, Yacc -  Yet Another  Compiler-Compiler,  Computer  Science
  3746.  
  3747.  
  3748.      Technical  Report  32, Bell Telephone Laboratories, Murray Hill, NJ, July
  3749.  
  3750.  
  3751.      1975.
  3752.  
  3753.  
  3754.  
  3755. [KlM89]
  3756.  
  3757.  
  3758.      E. Klein and M. Martin,  The  Parser  Generating  System  PGS,  Software-
  3759.  
  3760.  
  3761.      Practice & Experience 19, 11 (Nov. 1989), 1015-1028.
  3762.  
  3763.  
  3764.  
  3765. [Pen86]
  3766.  
  3767.  
  3768.      T. J. Pennello, Very Fast LR Parsing, SIGPLAN Notices 21, 7 (July  1986),
  3769.  
  3770.  
  3771.      145-151.
  3772.  
  3773.  
  3774.  
  3775. [Roeh76]
  3776.  
  3777.  
  3778.      J.  Rohrich,  Syntax-Error  Recovery  in   LR-Parsers,   in   Informatik-
  3779.  
  3780.  
  3781.  
  3782.  
  3783.  
  3784.  
  3785.  
  3786.  
  3787.  
  3788.  
  3789.  
  3790.  
  3791.  
  3792.                                                                             51
  3793.  
  3794.  
  3795.      Fachberichte, vol. 1, H.-J. Schneider and M. Nagl (ed.), Springer Verlag,
  3796.  
  3797.  
  3798.      Berlin, 1976, 175-184.
  3799.  
  3800.  
  3801.  
  3802. [Roeh80]
  3803.  
  3804.  
  3805.      J. Rohrich, Methods for the Automatic Construction  of  Error  Correcting
  3806.  
  3807.  
  3808.      Parsers, Acta Inf. 13, 2 (1980), 115-139.
  3809.  
  3810.  
  3811.  
  3812. [Roeh82]
  3813.  
  3814.  
  3815.      J. Rohrich, Behandlung syntaktischer Fehler,  Informatik  Spektrum  5,  3
  3816.  
  3817.  
  3818.      (1982), 171-184.
  3819.  
  3820.  
  3821.  
  3822. [WaG84]
  3823.  
  3824.  
  3825.      W. M. Waite and G. Goos,  Compiler  Construction,  Springer  Verlag,  New
  3826.  
  3827.  
  3828.      York, NY, 1984.
  3829.  
  3830.  
  3831.  
  3832. [WhH88]
  3833.  
  3834.  
  3835.      M. Whitney and R. N. Horspool, Extremely Rapid LR Parsing, in Proceedings
  3836.  
  3837.  
  3838.      of  the  Workshop  on  Compiler  Compiler  and High Speed Compilation, D.
  3839.  
  3840.  
  3841.      Hammer (ed.), Berlin, GDR, Oct. 1988, 248-257.
  3842.  
  3843.  
  3844.  
  3845.  
  3846.  
  3847.  
  3848.  
  3849.  
  3850.  
  3851.  
  3852.  
  3853.  
  3854.  
  3855.  
  3856.  
  3857.                                                                             52
  3858.  
  3859.  
  3860.                                    APPENDIX
  3861.  
  3862.                         Example of a Generated Parser
  3863.  
  3864. Grammar:
  3865.  
  3866.    L       : L S | .
  3867.    S       : 'i' '=' E .
  3868.    E       : E '+' E
  3869.            | E '-' E
  3870.            | E '*' E
  3871.            | E '/' E
  3872.            | 'i'
  3873.            | 'n'
  3874.            | '(' E ')' .
  3875.  
  3876.  
  3877.  
  3878. Parser:
  3879.  
  3880.  
  3881.  
  3882. # include "DynArray.h"
  3883. # define yyInitStackSize        100
  3884. # define yyNoState                0
  3885. # define yyTableMax             122
  3886. # define yyNTableMax            119
  3887. # define yyLastReadState         13
  3888. # define yyFirstReadReduce       14
  3889. # define yyFirstReduce           20
  3890. # define yyStartState             1
  3891. # define yyStopState             20
  3892. typedef short   yyStateRange;
  3893. typedef struct  { yyStateRange Check, Next; } yyTCombType;
  3894. typedef struct  { tScanAttribute Scan; } tParsAttribute;
  3895. static  yyTCombType *   yyTBasePtr      [yyLastReadState + 1];
  3896. static  yyStateRange *  yyNBasePtr      [yyLastReadState + 1];
  3897. static  yyStateRange    yyDefault       [yyLastReadState + 1];
  3898. static  yyTCombType     yyTComb         [yyTableMax + 1];
  3899. static  yyStateRange    yyNComb         [yyNTableMax + 1];
  3900. static  int             yyErrorCount;
  3901. int Parse ()
  3902. {
  3903.    register yyStateRange     yyState         ;
  3904.    register long             yyTerminal      ;
  3905.    register yyStateRange *   yyStateStackPtr ;
  3906.  
  3907.  
  3908.  
  3909.  
  3910.  
  3911.  
  3912.  
  3913.  
  3914.  
  3915.  
  3916.  
  3917.  
  3918.  
  3919.  
  3920.                                                                             53
  3921.    register tParsAttribute * yyAttrStackPtr  ;
  3922.    register bool             yyIsRepairing   ;
  3923.             unsigned long    yyStateStackSize = yyInitStackSize;
  3924.             unsigned long    yyAttrStackSize  = yyInitStackSize;
  3925.             yyStateRange *   yyStateStack    ;
  3926.             tParsAttribute * yyAttributeStack;
  3927.             tParsAttribute   yySynAttribute  ;       /* synthesized attribute */
  3928.             yyStateRange *   yyMaxPtr;
  3929.    yyState           = yyStartState;
  3930.    yyTerminal        = GetToken ();
  3931.    MakeArray (& yyStateStack, & yyStateStackSize, sizeof (yyStateRange));
  3932.    MakeArray (& yyAttributeStack, & yyAttrStackSize, sizeof (tParsAttribute));
  3933.    yyMaxPtr          = & yyStateStack [yyStateStackSize];
  3934.    yyStateStackPtr   = yyStateStack;
  3935.    yyAttrStackPtr    = & yyAttributeStack [1];
  3936.    yyErrorCount      = 0;
  3937.    yyIsRepairing     = false;
  3938. ParseLoop:
  3939.    for (;;) {
  3940.       if (yyStateStackPtr >= yyMaxPtr) {             /* stack overflow? */
  3941.          int StateStackPtr   = yyStateStackPtr - yyStateStack;
  3942.          int AttrStackPtr    = yyAttrStackPtr - yyAttributeStack;
  3943.          ExtendArray (& yyStateStack, & yyStateStackSize, sizeof (yyStateRange));
  3944.          ExtendArray (& yyAttributeStack, & yyAttrStackSize, sizeof (tParsAttribute));
  3945.          yyStateStackPtr     = yyStateStack + StateStackPtr;
  3946.          yyAttrStackPtr      = yyAttributeStack + AttrStackPtr;
  3947.          yyMaxPtr            = & yyStateStack [yyStateStackSize];
  3948.       };
  3949.       * yyStateStackPtr = yyState;
  3950. TermTrans:
  3951.       for (;;) {   /* SPEC State = Table (State, Terminal); terminal transition */
  3952.          register    yyStateRange * TCombPtr;
  3953.          TCombPtr = (yyStateRange *) (yyTBasePtr [yyState] + yyTerminal);
  3954.          if (* TCombPtr ++ == yyState) { yyState = * TCombPtr; break; };
  3955.          if ((yyState = yyDefault [yyState]) != yyNoState) goto TermTrans;
  3956.          /* error recovery */
  3957.       };
  3958.       if (yyState >= yyFirstReadReduce) {            /* reduce or read-reduce? */
  3959.          if (yyState < yyFirstReduce) {              /* read-reduce */
  3960.             yyStateStackPtr ++;
  3961.             (yyAttrStackPtr ++)->Scan = Attribute;
  3962.             yyTerminal = GetToken ();
  3963.             yyIsRepairing = false;
  3964.          };
  3965.          for (;;) {
  3966.             register long yyNonterminal;
  3967.             switch (yyState) {
  3968.  
  3969.  
  3970.  
  3971.  
  3972.  
  3973.  
  3974.  
  3975.  
  3976.  
  3977.  
  3978.  
  3979.  
  3980.  
  3981.  
  3982.                                                                             54
  3983.    case yyStopState: /* S' : L End-of-Tokens . */
  3984.       ReleaseArray (& yyStateStack, & yyStateStackSize, sizeof (yyStateRange));
  3985.       ReleaseArray (& yyAttributeStack, & yyAttrStackSize, sizeof (tParsAttribute));
  3986.       return yyErrorCount;
  3987.    case 21:
  3988.    case 19: /* L : L S . */
  3989.       yyStateStackPtr -= 2; yyAttrStackPtr -= 2; yyNonterminal = 111; break;
  3990.    case 22: /* L : . */
  3991.       yyStateStackPtr -= 0; yyAttrStackPtr -= 0; yyNonterminal = 111; break;
  3992.    case 23: /* S : 'i' '=' E . */
  3993.       yyStateStackPtr -= 3; yyAttrStackPtr -= 3; yyNonterminal = 112; break;
  3994.    case 24: /* E : E '+' E . */
  3995.       yyStateStackPtr -= 3; yyAttrStackPtr -= 3; yyNonterminal = 113; break;
  3996.    case 25: /* E : E '-' E . */
  3997.       yyStateStackPtr -= 3; yyAttrStackPtr -= 3; yyNonterminal = 113; break;
  3998.    case 26:
  3999.    case 17: /* E : E '*' E . */
  4000.       yyStateStackPtr -= 3; yyAttrStackPtr -= 3; yyNonterminal = 113; break;
  4001.    case 27:
  4002.    case 18: /* E : E '/' E . */
  4003.       yyStateStackPtr -= 3; yyAttrStackPtr -= 3; yyNonterminal = 113; break;
  4004.    case 28:
  4005.    case 14: /* E : 'i' . */
  4006.       yyStateStackPtr -= 1; yyAttrStackPtr -= 1; yyNonterminal = 113; break;
  4007.    case 29:
  4008.    case 15: /* E : 'n' . */
  4009.       yyStateStackPtr -= 1; yyAttrStackPtr -= 1; yyNonterminal = 113; break;
  4010.    case 30:
  4011.    case 16: /* E : '(' E ')' . */
  4012.       yyStateStackPtr -= 3; yyAttrStackPtr -= 3; yyNonterminal = 113; break;
  4013.             };
  4014.             /* SPEC State = Table (Top (), Nonterminal); nonterminal transition */
  4015.             yyState = * (yyNBasePtr [* yyStateStackPtr ++] + yyNonterminal);
  4016.             * yyAttrStackPtr ++ = yySynAttribute;
  4017.             if (yyState < yyFirstReadReduce) goto ParseLoop;
  4018.          };                                          /* read-reduce? */
  4019.       } else {                                       /* read */
  4020.          yyStateStackPtr ++;
  4021.          (yyAttrStackPtr ++)->Scan = Attribute;
  4022.          yyTerminal = GetToken ();
  4023.          yyIsRepairing = false;
  4024.       };
  4025.    };
  4026. };
  4027.  
  4028.  
  4029.  
  4030.  
  4031.  
  4032.  
  4033.  
  4034.  
  4035.  
  4036.  
  4037.  
  4038.  
  4039.  
  4040.  
  4041.  
  4042.  
  4043.  
  4044.  
  4045.  
  4046.                                                                              1
  4047.  
  4048.  
  4049. Contents
  4050.  
  4051.  
  4052.  
  4053.  
  4054.  
  4055. 1.      Introduction ....................................................    2
  4056.  
  4057.  
  4058. 2.      The Generated Parser ............................................    7
  4059.  
  4060.  
  4061. 2.1.    Basic LR-Parsing Algorithm ......................................    9
  4062.  
  4063.  
  4064. 2.2.    LR(0) Reductions ................................................   10
  4065.  
  4066.  
  4067. 2.3.    Encoding of the Table Entries ...................................   13
  4068.  
  4069.  
  4070. 2.4.    Semantic Actions and S-Attribution ..............................   14
  4071.  
  4072.  
  4073. 2.5.    Table Representation and Access .................................   20
  4074.  
  4075.  
  4076. 2.6.    Error Recovery ..................................................   24
  4077.  
  4078.  
  4079. 2.7.    Mapping Pseudo Code to C ........................................   25
  4080.  
  4081.  
  4082. 3.      The Parser Generator ............................................   29
  4083.  
  4084.  
  4085. 3.1.    Structure of Specification ......................................   30
  4086.  
  4087.  
  4088. 3.2.    Semantic Actions and S-Attribution ..............................   31
  4089.  
  4090.  
  4091. 3.3.    Ambiguous Grammars ..............................................   32
  4092.  
  4093.  
  4094. 3.4.    LR-Conflict Message .............................................   33
  4095.  
  4096.  
  4097. 3.5.    Error Recovery ..................................................   35
  4098.  
  4099.  
  4100.  
  4101.  
  4102.  
  4103.  
  4104.  
  4105.  
  4106.  
  4107.  
  4108.  
  4109.  
  4110.  
  4111.  
  4112.                                                                              2
  4113.  
  4114.  
  4115. 4.      Comparison of Parser Generators .................................   37
  4116.  
  4117.  
  4118. 5.      Conclusion ......................................................   47
  4119.  
  4120.  
  4121.         Acknowledgements ................................................   48
  4122.  
  4123.  
  4124.         References ......................................................   48
  4125.  
  4126.  
  4127.         Appendix ........................................................   52
  4128.  
  4129.  
  4130.  
  4131.  
  4132.  
  4133.  
  4134.  
  4135.  
  4136.  
  4137.  
  4138.  
  4139.  
  4140.  
  4141.  
  4142.  
  4143.  
  4144.  
  4145.  
  4146.  
  4147.  
  4148.  
  4149.  
  4150.  
  4151.  
  4152.  
  4153.  
  4154.  
  4155.  
  4156.  
  4157.  
  4158.  
  4159.  
  4160.  
  4161.  
  4162.  
  4163.  
  4164.  
  4165.  
  4166.  
  4167.  
  4168.  
  4169.  
  4170.  
  4171.